我怎样才能找到最大的两个小于没有循环的数字的幂?例子:
5 output = 4.
11 output = 8.
100 output = 64.
用 int 做的一个有点摆弄的方法是:
// given an int number (which would be the upper value of the range)
number |= number >> 1;
number |= number >> 2;
number |= number >> 4;
number |= number >> 8;
number |= number >> 16;
return (number >> 1) + 1;
这个想法是将 01010 之类的东西变成 01111,移位一次得到 00111,然后加一得到 01000 的结果。
要了解其工作原理,请考虑输入为 0 或非零。如果它是 0,那么结果是 0+1,因为所有的移位和 oring 只会导致 0(2 的 0 次方是 1,2 的最低幂,输入 0 的答案)。
对于非零数字,位中的某处是该值的最高有效设置位。我们想要的答案是该位没有跟踪它的所有次要有效位。所以专注于那一点,因为那是我们真正关心的。当我们对第一个右移 1 进行按位或运算时,我们确保设置的最高有效位仍然被设置,并且比它低 1 的位也被设置,因为 0100 | 0010 = 0110。接下来我们将其与自身进行 OR,但这次移动了 2。这确保我们有 MSB 加上 3 个尾随位。我们一直这样做,直到达到 int 中的位数限制。下面是一个完整的 32 位值示例的分步说明:
01000000110100111000000011010011
01000000000100000000000010000010 |
00100000000010000000000001000001 = 01100000000110000000000011000011 (num |= num >> 1)
01100000000110000000000011000011 |
00011000000001100000000000110000 = 01111000000111100000000011110011 (num |= num >> 2)
01111000000111100000000011110011 |
00000111100000011110000000001111 = 01111111100111111110000011111111 (num |= num >> 4)
01111111100111111110000011111111 |
00000000011111111001111111100000 = 01111111111111111111111111111111 (num |= num >> 8)
01111111111111111111111111111111 |
00000000000000000111111111111111 = 01111111111111111111111111111111 (num |= num >> 16)
现在剩下要做的就是将最终值右移一位,然后加一以将所有这些 1 变为 0,除了将我们所需答案的位设置为 1 的最高有效进位。
假设您想要小于 x 的 2 的最大幂,您可以从
Ans=pow(2,floor(log(x)/log(2)));
如果 n 是范围的最大数
return int(math.log(n, 2));
您可以采用基于 2 的数字的对数,这将为您提供权力,或者,如果它是一个整数,则获取最高位集的位置(但这可能需要一个循环)。
我有一段时间没有做过 C++,但在 C# 中它会是:
double val = 129;
int power = (int)(Math.Log10(val) / Math.Log10(2));
使用 log n (X) = log y (X) / log y (n)的规则
// This only works if i >= 0, and it may overestimate by one power of 2
// if i is slightly less than a large power of 2. Fixing these issues is
// not difficult, so it's left as an exercise.
long largest_power_of_two(long i) {
int exp;
frexp(double(i), &exp);
return long(ldexp(0.5, exp));
}
frexp
并且ldexp
只是摆弄比特,所以那里甚至没有隐藏循环(可能在微架构层除外)。
如果可用,您可以使用特殊的 CPU 指令,例如 x86 BSR
。
如果您可以在没有循环的情况下对数字进行位还原(也许,使用查找表或其他特殊的 CPU 指令),那么您可以还原数字,找到设置为 1 的最低有效位,重置其他位并将其还原。
unsigned IsolateLeastSignificantOne(unsigned n)
{
return n & -n;
}
n = BitRevert(IsolateLeastSignificantOne(BitRevert(n)));
有关小技巧和其他有用的信息,请参阅此文档。