我有一个无符号的64位数字,代表尾数,或分数(代表从[0..1)0的范围,其中0.0映射到0,0xffffff..映射到"恰好在1.0之前"的数字)

现在我想把这个范围分成相等的buckets-并回答-给定随机数key,它将落到范围的哪一部分?

通过以下代码可以更轻松地获得:

func BucketIndex(key, buckets uint64) uint64 {
    return uint64(float64(key) / ((math.Pow(2, 64) / float64(buckets)))
}

我的try 是将2^64一分为二,就像我要将范围缩小到32位一样,并在64位中进行运算以进行数学运算:

// ~=key / ((1 << 64) / buckets)
return ((key >> 32) * buckets) >> 32

但射程不再相等.. 三分之一的人将达到0x5555555600000000岁,而不是0x5555555555555556岁 这是一个令人悲伤的故事,所以我问你有没有更好的方法来找到(1 << 64) / buckets

推荐答案

如果buckets是(编译时)常量,则可以使用constant expression来计算存储桶大小:常量可以是任意大小.否则,您可以在运行时使用big.Int来计算它,并存储结果(这样您就不必一直使用big.Int计算).

在编译时使用常量表达式

要实现整数除法四舍五入,请将除数-1加到被除数:

const (
    max        = math.MaxUint64 + 1
    buckets    = 3
    bucketSize = uint64((max + buckets - 1) / buckets)
)

Using big.Int, at runtime

我们也可以对big.Int使用上述相同的逻辑.另一种方法是使用Int.DivMod()(而不是加buckets -1),如果mod大于零,则将结果递增1.

func calcBucketSize(max, buckets *big.Int) uint64 {
    max = max.Add(max, buckets)
    max = max.Add(max, big.NewInt(-1))
    return max.Div(max, buckets).Uint64()
}

var bucketSize = calcBucketSize(new(big.Int).SetUint64(math.MaxUint64), big.NewInt(3))

Go相关问答推荐

从Kafka到Clickhouse的实时消费数据

GetSecretValue,get identity:get credentials:无法刷新缓存的凭据

如何在使用中介资源时处理函数中的`defer`

GORM没有从 struct 创建完整的表,如何修复?

map 中的多个函数类型,Golang

如何防止程序B存档/删除围棋中程序A当前打开的文件?

在nixos上找不到XInput2.h头文件的包

重新赋值变量时未清除动态类型-这是错误吗?

生成一个 CSV/Excel,在 Golang 中该列的下拉选项中指定值

Kperf 构建失败

用于提取 <*n 的正则表达式(其中 n 是一个数字)

同一文件上的多个 Arrow CSV 阅读器返回 null

在反向 GORM 中创建查询有一个关系

使用 go.work 文件在多个测试文件上运行 go test 命令

如何在 Windows 中使用 github.com/AllenDang/giu 和 github.com/gordonklaus/portaudio 构建 GO 程序

实现接口的指针的泛型类型是什么?

GOENV 只能使用 OS 环境设置

处理程序中的无限循环

comparable和any有什么区别?

如何将类型转换为字节数组golang