【问题标题】:How to subdivide integer hash into ranges如何将整数哈希细分为范围
【发布时间】:2023-02-06 23:52:48
【问题描述】:

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

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

从以下代码中更容易获得:

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

我试图“解决这个问题”——将 2^64 分成两个,就像我将范围缩小到 32 位,并在 64 位中运行以进行数学运算一样:

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

但范围停止相等.. 例如,三分之一 (buckets==3) 将在0x5555555600000000,而不是在0x5555555555555556 那是个悲伤的故事,所以我问你知道找到(1 &lt;&lt; 64) / buckets 的更好方法吗?

【问题讨论】:

  • 使用 max / buckets 的桶大小,四舍五入,桶索引将为 key / bucketSize。这还不够吗?
  • @icza 这是我的问题,你怎么能找到 max (在 uint64 范围之外)
  • 换句话说,我对 (Maxint64+1) / buckets 感兴趣
  • 想一想 key / (max / buckets) 如果你会做 key * buckets / max - 你会立即得到 0,因为它就像将 uint64 的所有位移动 64 个位置到 lsb,从 uint64 存储中清除它的所有位......

标签: go fixed-point


【解决方案1】:

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

在编译时使用常量表达式

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

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

在运行时使用big.Int

我们也可以对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 max, _ = new(big.Int).SetString("18446744073709551616", 10)
var bucketSize = calcBucketSize(max, big.NewInt(3))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    • 2022-07-20
    • 2015-11-23
    • 1970-01-01
    • 2021-02-24
    • 1970-01-01
    相关资源
    最近更新 更多