判断一个数是不是2的幂

我们经常会遇到这样一个问题,就是判断某个数据是否为2的n次方(1,2,4,8,16...)。例如如果用户输入的不是2^n,则要求用户重新输入。为了说明这种判断算法,我首先构造一个测试程序,代码如下:

#include <stdio.h>
#include <time.h>

int main()
{
    long unsigned int i;
    clock_t start,end;
    start = clock();
    for(i=1;i<100000000;i++)
    {
        if(is2Power(i))
        {
            printf("%ld\n",i);
        }
    }

    end =clock();
    printf("The total time is: %lf",((double)(end-start))/CLOCKS_PER_SEC);
    return 0;
}

一般人很容易想到下面的算法:

int is2Power(long unsigned int num)
{
    long unsigned int i;
    for(i=1;i<=num;i*=2)
    {
        if(i == num)
        {
             return 1;
        }
    }
    return 0;
}

该算法很容易理解,但是效率不算特别高,在我的机器上的测试结果为:The total time is: 28.566000,有人提出,其中的i*=2,可以用i<<=1来替换,效率应该会高一些,我们可以尝试一下:

int is2Power(long unsigned int num)
{
    long unsigned int i;
    for(i=1;i<=num;i<<=1)
    {
        if(i == num)
        {
             return 1;
        }
    }
    return 0;
}

在我的机器上的测试结果为:The total time is: 27.821000,我们发现仅仅提高了不到一秒,基本可以忽略不计。又有人提出,我们可以采取另一种思路,让i从num开始每次除2来判断,如果余数不为0立即返回,这样能会快判断出那些不符合条件的值,这样便能加快判断速度。

int is2Power(long unsigned int num)
{
    long unsigned int i;
    for(i=num;i>=1;i/=2)
    {
        if(i==1)
            return 1;
        else if(i%2 != 0)
        {
             return 0;
        }
    }
    return 1;
}

这种算法在我的机器上的测试结果为:The total time is: 3.958000,可以看出效率提高了近8倍。这时又有大神提出,由于2的n次方的数二进制表示是第1位为1,其余为0,而x-1(假如x为2的n次方)得到的数的二进制表示恰恰是第1位为0,其余为1,两者相与,得到的结果就为0,否则结果肯定不为0。于是诞生了如下算法:

int is2Power(long unsigned int num)
{
    return ((num & (num-1))==0)?1:0;
}

简单的有点吓人,而且效率也很高,在我的机器上的测试结果为:The total time is: 1.484000,当然也有一个类似的算法,原理类似:

int is2Power(long unsigned int num)
{
    return ((num & (~num+1))==num)?1:0;
}

这种算法在我的机器上的测试结果为:The total time is: 1.503000

很简单的问题,只要我们仔细研究一下还是有不少收获的。

注:文中的算法仅说明了相对的效率问题,鲁棒性并没有测试,如果你在实际情况中使用,需要注意一下边界值和反常输入情况。

posted @ 2014-05-17 19:06  Aloys_Code  阅读(428)  评论(0编辑  收藏  举报
我的GITHUB|