【问题标题】:Quick integer logarithm for special case特殊情况的快速整数对数
【发布时间】:2021-01-26 23:25:53
【问题描述】:

我有介于 32-8191 之间的整数值,我想将其映射到大致对数刻度。如果我使用基数 2,我可以只计算前导零位并将它们映射到 8 个插槽中,但这太粗略了;我需要 32 个插槽(更多会更好,但我需要它们映射到 32 位值中的位),其对数的底数约为 1.18-1.20。有人有一些技巧可以非常快速地计算这个值或合理的近似值吗?

我的直觉是使用条件将范围分解为 2 或 3 个子范围,并为每个子范围使用一个小的查找表,但我想知道是否有一些技巧可以用 count-leading-zeros 然后细化结果,特别是因为结果不必是精确的,而只是大致对数。

【问题讨论】:

    标签: c optimization integer bit-manipulation logarithm


    【解决方案1】:

    为什么不使用前两位而不是前两位。您可以先将数字划分为 8 个 bin,然后将接下来的两位进一步划分为每个 bin 为 4。在这种情况下,您可以使用非常快速的简单移位操作。

    编辑:如果您认为使用对数是可行的解决方案。这是一般算法:

    a为对数的底,取值范围为(b_min, b_max) = (32,8191)。您可以使用以下公式找到基数:

    log(b_max/b_min) / log(a) = 32 bin
    

    给你a~1.1892026。如果使用此 a 作为对数的底,则可以将范围 (b_min, b_max) 映射到 (log_a(b_min), log_a(b_max)) = (20.0004,52.0004)

    现在您只需将所有元素减去20.0004 即可得到范围(0,32)。它保证所有元素都是对数一致的。完成

    注意:任何一个元素都可能因为数值错误而超出范围。你应该自己计算它的确切值。

    注2:log_a(b) = log(b)/log(a)

    【讨论】:

    • 我见过以前做过的事情(想到 dlmalloc),但我不知道我是否喜欢它偏离对数的程度。不过也许没那么糟糕。
    【解决方案2】:

    表查找是一种选择,该表不是那么大。如果 8K 表太大,并且您有计数前导零指令,则可以使用前几位的表查找。

    nbits = 32 - count_leading_zeros(v)  # number of bits in number
    highbits = v >> (nbits - 4)          # top 4 bits.  Top bit is always a 1.
    log_base_2 = nbits + table[highbits & 0x7]
    

    您使用 log_2 的近似值填充的表

    table[i] = approx(log_2(1 + i/8.0))
    

    如果您想继续进行整数运算,请将最后一行乘以一个方便的因子。

    【讨论】:

    • 8k 表太大了,但包含 8-32 个条目的表也不错。不过,我喜欢 hwlau 解决方案的简单性。
    • 我认为我们的解决方案实际上是相同的(我的使用接下来的 3 位,但这是可配置的)。
    【解决方案3】:

    我刚刚提出的基于 IEEE 754 浮点的答案:

    ((union { float v; uint32_t r; }){ x }.r >> 21 & 127) - 16
    

    它将 32-8192 大致以对数方式映射到 0-31(与 hwlau 的答案相同)。

    改进版(去掉无用的按位和):

    ((union { float v; uint32_t r; }){ x }.r >> 21) - 528
    

    【讨论】:

    • 如何重新缩放到其他数量的 bin?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 2020-05-11
    相关资源
    最近更新 更多