我有32-8191的整数值,我想要映射到一个大致对数的比例。如果我使用base 2,我可以计算前导零位并将它们映射到8个插槽,但这也是过程粗糙的;我需要32个插槽(更多会更好,但我需要它们映射到32位值中的位),这对于对数大约为1.18-1.20。任何人都有一些技巧来计算这个值,或者是一个合理的近似值,非常快?
我的直觉是用条件将范围分解为2或3个子范围,并为每个使用一个小的查找表,但我想知道是否有一些技巧我可以用count-leading-zeros然后改进结果,特别是因为结果不一定是精确的,而只是大致对数。
答案 0 :(得分:4)
为什么不使用前导位以外的下两位。您可以先将数字划分为8 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)
。它保证所有元素都是对数均匀的。完成
注意:由于数字错误,元素可能会超出范围。你应该自己计算确切的值。
Note2 :log_a(b)= log(b)/ log(a)
答案 1 :(得分: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))
如果你想保持整数运算,请将最后一行乘以一个方便的因子。
答案 2 :(得分:2)
我刚刚提出的基于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