判断一个数是不是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
很简单的问题,只要我们仔细研究一下还是有不少收获的。
注:文中的算法仅说明了相对的效率问题,鲁棒性并没有测试,如果你在实际情况中使用,需要注意一下边界值和反常输入情况。