刚接触这个命题的时候,简单地想了想——很简单:
第一个方案:除2法。将它一直除以2,最后得到1则标识这个数是2的正整次方,得到一个小于1的小数则不是。
第一个方案:除2法。将它一直除以2,最后得到1则标识这个数是2的正整次方,得到一个小于1的小数则不是。
后来一想,这样的算法效率肯定不敢恭维,于是就有了下面的初步改进版:除2法的基础上进行奇偶判定。
private static bool IsPowerOfTwo_Obsolete(ulong value) { double tmp = 0; while (value > 2) { /* * 若 value 的值是2的正整数次方,则一直除以2的操作得到的数,必定仍然为偶数,直到为 1。 * 反之,若 value 的值不是2的正整数次方,则除以2的操作必定在某一步的时候会产生一个奇数,假定这个奇数是N * 此时 N 除以2得到的浮点数必定和 N 除以2得到的整数值不相等。 */ tmp = Convert.ToDouble(value) / 2; if (tmp % 2 != 0) { return false; } else { value = value / 2; } } return value == 2; }
其实写到这里,这个方法已经不错了。但后来继续挖掘信息,又一次改进了方法:进行二进制位比较,效率得到了大幅的提高:
//检查是否为2的正整数次方 private static bool IsPowerOfTwo_Obsolete2(ulong value) { if (value > 1) { /* * 针对每一个 ulong 的正整数,如果是2的正整数次方,则转换为二进制表示时一定有且仅有一个“1”存在。 * 基于以上原理,则可以按位来比较。 * 从低位到高位查询,遇到“1”后停止比较,所得到的数应当与原数相等, * 否则该数的二进制形式不止一个“1”位,即该数一定不是2的正整数次方。 */ ulong mask = 1ul; for (int count = 0;count < 64;count++) { //检查当前标识位是否为“0” if ((mask & value) == 0) { //当前位为“0”,则左移标识位,再次检查 mask <<= 1; continue; } /* * 程序执行到此,表明当前标识位为“1”,停止检查。 * 而此时的 mask 值正好应当与 value 相等,否则 value 就不是2的正整数次方。 */ return mask == value; } } return false; }
感谢网友gbb21,他在此帖中提到使用如下语句,算是把这个执行效率提高到终极高度:
////// 检查是否为2的正整数次方 /// /// /// private static bool IsPowerOfTwo(ulong value) { if (value > 1) { return ((value - 1) & value) == 0; } return false; }
到此为止,我想这个算法应该没多大提升空间了吧…