如何快速找到二进制对数? (O(1)充其量)

时间:2010-04-19 14:34:50

标签: algorithm math logarithm numerical-methods

有没有非常快的方法来查找整数的二进制对数?例如,给定一个数字 x = 52656145834278593348959013841835216159447547700274555627155488768这样的算法必须找到y = log(x,2),即215. x总是2的幂。

问题似乎很简单。所需要的只是找到最重要的1位的位置。有一个众所周知的方法FloorLog,但它不是很快,特别是对于很长的多字整数。

最快的方法是什么?

7 个答案:

答案 0 :(得分:23)

Bit Twiddling Hacks您要找的是什么?

答案 1 :(得分:7)

快速破解:大多数浮点数表示会自动规范化值,这意味着它们可以在硬件中有效地执行循环Christoffer Hammarström mentioned。因此,如果数字在FP表示的指数范围内,那么只需将整数转换为FP并提取指数即可。 (在您的情况下,您的整数输入需要多个机器字,因此需要在转换中执行多个“移位”。)

答案 2 :(得分:4)

如果整数存储在uint32_t a[]中,那么我的明显解决方案如下:

  1. a[]上运行线性搜索,以在uint32_t中找到具有最高值的非零a[i]a[](使用uint64_t进行测试对于该搜索,如果您的计算机具有本机uint64_t支持

  2. 应用bit twiddling hacks查找您在步骤1中找到的buint32_t的二进制日志a[i]

  3. 评估32*i+b

答案 3 :(得分:2)

答案是实现或语言相关。任何实现都可以存储有效位的数量以及数据,因为它通常是有用的。如果必须计算,则找到最重要的单词/肢体和该单词中最重要的位。

答案 4 :(得分:0)

通过使用二进制搜索,我头脑中的最佳选择是O(log(logn))方法。以下是64位(<= 2^63 - 1)数字(在C ++中)的示例:

int log2(int64_t num) {
    int res = 0, pw = 0;    
    for(int i = 32; i > 0; i --) {
        res += i;
        if(((1LL << res) - 1) & num)
            res -= i;
    }
    return res;
}

这个算法基本上会给我提供最高数量的资源,例如(2^res - 1 & num) == 0。当然,对于任何数字,你都可以在类似的事情中解决这个问题:

int log2_better(int64_t num) {
    var res = 0;
    for(i = 32; i > 0; i >>= 1) {
        if( (1LL << (res + i)) <= num )
            res += i;
    }
    return res;
}

请注意,此方法依赖于“bitshift”操作或多或少为O(1)的事实。如果不是这种情况,则必须预先计算2的所有幂,或者2^2^i形式的数字(2 ^ 1,2 ^ 2,2 ^ 4,2 ^ 8等)和做一些乘法(在这种情况下不是O(1))。

答案 5 :(得分:0)

您可以事先创建一个对数数组。这将找到最多log(N)的对数值:

#define N 100000
int naj[N];

naj[2] = 1;
for ( int i = 3; i <= N; i++ )
{
    naj[i] = naj[i-1];
    if ( (1 << (naj[i]+1)) <= i )
        naj[i]++;

}

数组naj是您的对数值。其中naj [k] = log(k)。 日志基于两个。

答案 6 :(得分:0)

这使用二进制搜索来找到最接近2的幂。

public static int binLog(int x,boolean shouldRoundResult){
    // assuming 32-bit integer
    int lo=0;
    int hi=31;
    int rangeDelta=hi-lo;
    int expGuess=0;
    int guess;
    while(rangeDelta>1){
        expGuess=(lo+hi)/2; // or (loGuess+hiGuess)>>1
        guess=1<<expGuess;
        if(guess<x){
            lo=expGuess;
        } else if(guess>x){
            hi=expGuess;            
        } else {
            lo=hi=expGuess;
        }
        rangeDelta=hi-lo;
    }
    if(shouldRoundResult && hi>lo){
        int loGuess=1<<lo;
        int hiGuess=1<<hi;
        int loDelta=Math.abs(x-loGuess);
        int hiDelta=Math.abs(hiGuess-x);
        if(loDelta<hiDelta)
            expGuess=lo;
        else
            expGuess=hi;
    } else {
        expGuess=lo;
    }
    int result=expGuess;
    return result;
}