如何判断一个数是否是2的幂
今天,我需要一个简单的算法来检查一个数字是否是 2 的幂。
该算法需要是:
- 简单
- 正确任何
ulong
值。
我想出了这个简单的算法:
private bool IsPowerOfTwo(ulong number)
{
if (number == 0)
return false;
for (ulong power = 1; power > 0; power = power << 1)
{
// This for loop used shifting for powers of 2, meaning
// that the value will become 0 after the last shift
// (from binary 1000...0000 to 0000...0000) then, the 'for'
// loop will break out.
if (power == number)
return true;
if (power > number)
return false;
}
return false;
}
但后来我想:检查 log2 x 是否恰好是一个整数怎么样? 当我检查 2^63+1 时,由于四舍五入,Math.Log() 恰好返回 63。 所以我检查了 2 的 63 次方是否等于原始数字,确实如此,因为计算是在 double 中完成的,而不是精确数字。
private bool IsPowerOfTwo_2(ulong number)
{
double log = Math.Log(number, 2);
double pow = Math.Pow(2, Math.Round(log));
return pow == number;
}
对于给定的错误值,这返回了 true
:9223372036854775809
。
有更好的算法吗?
Today I needed a simple algorithm for checking if a number is a power of 2.
The algorithm needs to be:
- Simple
- Correct for any
ulong
value.
I came up with this simple algorithm:
private bool IsPowerOfTwo(ulong number)
{
if (number == 0)
return false;
for (ulong power = 1; power > 0; power = power << 1)
{
// This for loop used shifting for powers of 2, meaning
// that the value will become 0 after the last shift
// (from binary 1000...0000 to 0000...0000) then, the 'for'
// loop will break out.
if (power == number)
return true;
if (power > number)
return false;
}
return false;
}
But then I thought: How about checking if log2 x is an exactly a round number? When I checked for 2^63+1, Math.Log()
returned exactly 63 because of rounding. So I checked if 2 to the power 63 is equal to the original number and it is, because the calculation is done in double
s and not in exact numbers.
private bool IsPowerOfTwo_2(ulong number)
{
double log = Math.Log(number, 2);
double pow = Math.Pow(2, Math.Round(log));
return pow == number;
}
This returned true
for the given wrong value: 9223372036854775809
.
Is there a better algorithm?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
对于这个问题有一个简单的技巧:
注意,此函数将为
0
报告true
,这不是2
的幂。 如果您想排除它,请执行以下操作:解释
首先也是最重要的是按位二进制 & MSDN 定义中的运算符:
现在让我们看一下这一切是如何进行的:
该函数返回布尔值(true / false)并接受一个 unsigned long 类型(在本例中为 x)的传入参数。 为了简单起见,让我们假设有人传递了值 4 并像这样调用该函数:
现在我们将每次出现的 x 替换为 4:
我们已经知道 4 != 0 的值为 true,到目前为止一切顺利。 但是呢:
这当然可以翻译成:
但是
4&3
到底是什么?4 的二进制表示形式是 100,3 的二进制表示形式是 011(请记住 & 采用这些数字的二进制表示形式)。 所以我们有:
想象一下这些值像基本加法一样堆叠起来。
&
运算符表示如果两个值都等于 1,则结果为 1,否则结果为 0。因此1 & 1 = 1,
1 & 0 = 0
,0 & 0 = 0
,以及0 & 1 = 0。 所以我们计算一下:
结果就是 0。所以我们回去看看 return 语句现在翻译成什么:
现在翻译成:
我们都知道
true && true
就是true
,这表明在我们的示例中,4 是 2 的幂。There's a simple trick for this problem:
Note, this function will report
true
for0
, which is not a power of2
. If you want to exclude that, here's how:Explanation
First and foremost the bitwise binary & operator from MSDN definition:
Now let's take a look at how this all plays out:
The function returns boolean (true / false) and accepts one incoming parameter of type unsigned long (x, in this case). Let us for the sake of simplicity assume that someone has passed the value 4 and called the function like so:
Now we replace each occurrence of x with 4:
Well we already know that 4 != 0 evals to true, so far so good. But what about:
This translates to this of course:
But what exactly is
4&3
?The binary representation of 4 is 100 and the binary representation of 3 is 011 (remember the & takes the binary representation of these numbers). So we have:
Imagine these values being stacked up much like elementary addition. The
&
operator says that if both values are equal to 1 then the result is 1, otherwise it is 0. So1 & 1 = 1
,1 & 0 = 0
,0 & 0 = 0
, and0 & 1 = 0
. So we do the math:The result is simply 0. So we go back and look at what our return statement now translates to:
Which translates now to:
We all know that
true && true
is simplytrue
, and this shows that for our example, 4 is a power of 2.一些记录和解释这种和其他一些琐碎黑客的网站是:
(http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2< /a>)
(http://bits.stephan-brumme.com/isPowerOfTwo.html
)他们的祖父,小亨利·沃伦 (Henry Warren, Jr.) 的书《黑客的乐趣》:
作为Sean Anderson 的页面 解释了表达式
((x & (x - 1)) == 0)
错误地指出 0 是 2 的幂。他建议使用:来纠正该问题。
Some sites that document and explain this and other bit twiddling hacks are:
(http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2)
(http://bits.stephan-brumme.com/isPowerOfTwo.html)
And the grandaddy of them, the book "Hacker's Delight" by Henry Warren, Jr.:
As Sean Anderson's page explains, the expression
((x & (x - 1)) == 0)
incorrectly indicates that 0 is a power of 2. He suggests to use:to correct that problem.
返回(i & -i) == i
return (i & -i) == i
以下对已接受答案的补充可能对某些人有用:
以二进制表示时,2 的幂总是看起来像 1 后跟 n 个零,其中 n 大于或等于 0。例如:
等等。
当我们从这些数字中减去
1
时,它们会变成 0 后跟 n 个,并且 n 与上面相同。 例如:等等。
来到症结所在
x
的 1 与x - 1
的零对齐,并且x
的所有零与x
的 1 对齐>x - 1,导致按位与结果为 0。 这就是我们如何得到上面提到的单行答案是正确的。进一步增加了上面接受的答案的美感 -
所以,我们现在有一个可以使用的财产:
此属性的一个很棒的用途是找出 - 如何给定数字的二进制表示形式中存在许多 1? 对于给定整数
x
执行此操作的简短而有趣的代码是:可以从概念证明的数字的另一个方面上面解释的是“每个正数都可以表示为2的幂和吗?”。
是的,每个正数都可以表示为 2 的幂之和。对于任何数字,取其二进制表示形式。 例如:取号码
117
。The following addendum to the accepted answer may be useful for some people:
A power of two, when expressed in binary, will always look like 1 followed by n zeroes where n is greater than or equal to 0. Ex:
and so on.
When we subtract
1
from these kind of numbers, they become 0 followed by n ones and again n is same as above. Ex:and so on.
Coming to the crux
The one of
x
gets aligned with the zero ofx - 1
and all the zeroes ofx
get aligned with ones ofx - 1
, causing the bitwise AND to result in 0. And that is how we have the single line answer mentioned above being right.Further adding to the beauty of accepted answer above -
So, we have a property at our disposal now:
One awesome use of this property is in finding out - How many 1s are present in the binary representation of a given number? The short and sweet code to do that for a given integer
x
is:Another aspect of numbers that can be proved from the concept explained above is "Can every positive number be represented as the sum of powers of 2?".
Yes, every positive number can be represented as the sum of powers of 2. For any number, take its binary representation. Ex: Take number
117
.这是一个简单的 C++ 解决方案:
Here's a simple C++ solution:
发布问题后,我想到了以下解决方案:
我们需要检查二进制数字中的一位是否为 1。 因此,我们只需将数字一次右移一位,如果等于 1,则返回
true
。如果在任何时候我们得到一个奇数 ((number & 1) == 1
),我们知道结果是false
。 事实证明(使用基准测试),对于(大)真值,这比原始方法稍快,而对于假值或小值,则快得多。当然,格雷格的解决方案要好得多。
After posting the question I thought of the following solution:
We need to check if exactly one of the binary digits is one. So we simply shift the number right one digit at a time, and return
true
if it equals 1. If at any point we come by an odd number ((number & 1) == 1
), we know the result isfalse
. This proved (using a benchmark) slightly faster than the original method for (large) true values and much faster for false or small values.Of course, Greg's solution is much better.
这里有一个通用算法,用于确定一个数字是否是另一个数字的幂。
And here's a general algorithm for finding out if a number is a power of another number.
现在在 .Net 6 中这非常容易。
这里是文档。
It's very easy in .Net 6 now.
Here is the documentation.
这真的很快。 检查所有 2^32 个整数大约需要 6 分 43 秒。
This is really fast. It takes about 6 minutes and 43 seconds to check all 2^32 integers.
如果
x
是2的幂,则其唯一的1位位于位置n
。 这意味着x – 1
在位置n
处有一个 0。 要了解原因,请回忆一下二进制减法的工作原理。 当从x
中减1时,借位一直传播到位置n
; 位n
变为 0,所有低位变为 1。现在,由于x
没有与x – 1
相同的 1 位,x& (x – 1)
为 0,并且!(x & (x – 1))
为 true。If
x
is a power of two, its lone 1 bit is in positionn
. This meansx – 1
has a 0 in positionn
. To see why, recall how a binary subtraction works. When subtracting 1 fromx
, the borrow propagates all the way to positionn
; bitn
becomes 0 and all lower bits become 1. Now, sincex
has no 1 bits in common withx – 1
,x & (x – 1)
is 0, and!(x & (x – 1))
is true.对于 2 的任何幂,以下也成立。
n&(-n)==n
注意:对于 n=0 失败,因此需要检查它
这样做的原因是:
-n 是 n 的 2s 补码。 -n 将使 n 的最右边设置位左侧的每一位与 n 相比翻转。 对于 2 的幂,只有一位设置位。
for any power of 2, the following also holds.
n&(-n)==n
NOTE: fails for n=0 , so need to check for it
Reason why this works is:
-n is the 2s complement of n. -n will have every bit to the left of rightmost set bit of n flipped compared to n. For powers of 2 there is only one set bit.
判断给定的数字是否是 2 的幂。
Find if the given number is a power of 2.
如果一个数字仅包含 1 个设置位,则该数字是 2 的幂。 我们可以使用这个属性和通用函数
countSetBits
来判断一个数字是否是 2 的幂。这是一个 C++ 程序:
我们不需要显式检查 0 是否为 2 的幂,因为它对于 0 也返回 False。
输出
A number is a power of 2 if it contains only 1 set bit. We can use this property and the generic function
countSetBits
to find if a number is power of 2 or not.This is a C++ program:
We dont need to check explicitly for 0 being a Power of 2, as it returns False for 0 as well.
OUTPUT
这是我设计的另一种方法,在本例中使用
|
而不是&
:Here is another method I devised, in this case using
|
instead of&
:我一直在阅读 Random.nextInt(intbound) 的文档,看到了这段不错的代码,它检查参数是否是 2 的幂,其中写着(代码的一部分):
让我们测试一下
你注意到了吗某物 ? 正负 2 次方数字在二进制表示中具有相反的位(负 2 次方数字是 1 的补码 的正 2 次方数。
如果我们对 X 和 -X 进行逻辑与(其中 X 是 2 次方数),我们会得到相同的正值 X :)
I've been reading the documentation for Random.nextInt(int bound) and saw this nice piece of code which checks whether the parameter is a power of 2, which says (part of the code) :
let's test it
did you notice something ? positive and negative Power 2 numbers have opposite bits in binary representation (negative power 2 numbers are 1's complement of positive power 2 numbers.
if we apply a logical AND over X and -X, where X is a Power 2 number, we get the same positive value X as a result :)
示例
算法
使用位掩码,将
NUM
变量除以二进制IF R > 0且L> 0:返回FALSE
否则,
NUM
变为非零IF NUM = 1:返回 TRUE
否则,转到步骤 1
复杂性
时间 ~
O(log(d))
其中d
是二进制位数Example
Algorithm
Using a bit mask, divide
NUM
the variable in binaryIF R > 0 AND L > 0: Return FALSE
Otherwise,
NUM
becomes the one that is non-zeroIF NUM = 1: Return TRUE
Otherwise, go to Step 1
Complexity
Time ~
O(log(d))
whered
is number of binary digits如果您有 .NET Core,Markgravell 建议这个 3、系统。 Runtime.Intrinsics.X86.Popcnt.PopCount
单指令,比
(x != 0) && 更快 ((x & (x - 1)) == 0)
但便携性较差。Mark gravell suggested this if you have .NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount
Single instruction, faster than
(x != 0) && ((x & (x - 1)) == 0)
but less portable..NET 6 中有一个衬垫
There is a one liner in .NET 6
改进@user134548的答案,无需位算术:
这适用于:
Improving the answer of @user134548, without bits arithmetic:
This works fine for:
在这种方法中,您可以检查整数中是否只有 1 个设置位,并且该整数是否大于 1。 0(c++)。
in this approach , you can check if there is only 1 set bit in the integer and the integer is > 0 (c++).
Kotlin:
或
inv 反转该值中的位。
注:
log2 解决方案不适用于大数字,例如 536870912 ->
Kotlin:
or
inv inverts the bits in this value.
Note:
log2 solution doesn't work for large numbers, like 536870912 ->
我看到很多答案都建议返回 n && !(n & (n - 1)) 但根据我的经验,如果输入值为负数,它会返回错误值。
我将在这里分享另一种简单的方法,因为我们知道两个数字的幂只有一个设置位,所以我们只需计算设置位的数量,这将花费 O(log N) 时间。
检查这篇文章计数。 设置位
I see many answers are suggesting to return n && !(n & (n - 1)) but to my experience if the input values are negative it returns false values.
I will share another simple approach here since we know a power of two number have only one set bit so simply we will count number of set bit this will take O(log N) time.
Check this article to count no. of set bits
这也是另一种方法
This is another method to do it as well
如果数字是 2 的幂,最多 64 个值,则返回此值(您可以在 for 循环条件中更改它(“6”表示 2^6 是 64);
This one returns if the number is the power of two up to 64 value ( you can change it inside for loop condition ("6" is for 2^6 is 64);
有许多答案和发布的链接解释了为什么
n & (n-1) == 0
适用于 2 的幂,但我找不到任何解释为什么它不适用于非 2 的幂,所以我'我添加这个只是为了完整性。对于 n = 1 (2^0 = 1), 1 & 0 = 0,所以我们很好。
对于奇数 n > 1,至少有 2 位为 1(最左边和最右边的位)。 现在 n 和 n-1 仅在最右边的位上有所不同,因此它们的 &-sum 最左边的位至少有一个 1,因此 n & (n-1) != 0:
现在,即使 n 不是 2 的幂,我们也至少有 2 位 1(最左边和非最右边)。 这里,n 和 n-1 的差异最多为最右边的 1 位,因此它们的 &-sum 最左边的位也至少有一个 1:
There were a number of answers and posted links explaining why the
n & (n-1) == 0
works for powers of 2, but I couldn't find any explanation of why it doesn't work for non-powers of 2, so I'm adding this just for completeness.For n = 1 (2^0 = 1), 1 & 0 = 0, so we are fine.
For odd n > 1, there are at least 2 bits of 1 (left-most and right-most bits). Now n and n-1 will only differ by the right-most bit, so their &-sum will at least have a 1 on the left-most bit, so
n & (n-1) != 0
:Now for even n that is not a power of 2, we also have at least 2 bits of 1 (left-most and non-right-most). Here, n and n-1 will differ up to the right-most 1 bit, so their &-sum will also have at least a 1 on the left-most bit:
我假设 1 是 2 的幂,确实如此,它是 2 的零次方
I'm assuming 1 is a power of two, which it is, it's 2 to the power of zero
在 C 语言中,我测试了 i && !(i & (i - 1) 技巧,并将其与
__builtin_popcount(i)
进行比较,在 Linux 上使用 gcc,并使用 -mpopcnt 标志确保使用 CPU 的 POPCNT 指令我的测试程序计算了区间 [0, 2^31) 中 2 的幂的整数数量。起初我以为
i && !(i & (i - 1)
速度快了 10%,尽管我验证了在我使用__builtin_popcount
的反汇编中使用了 POPCNT。但是,我意识到我已经包含了if 语句和分支预测在位旋转版本上可能做得更好,我删除了 if 和 POPCNT 结果更快
:
Intel(R) Core(TM) i7-4771 CPU max 3.90GHz 。
AMD Ryzen Threadripper 2950X 16 核处理器最高频率 3.50GHz
请注意,这里 Intel CPU 的速度似乎比 AMD 稍慢,但 POPCNT 却比 AMD POPCNT 快得多; 提升。
t 提供尽可能
多的
In C, I tested the
i && !(i & (i - 1)
trick and compared it with__builtin_popcount(i)
, using gcc on Linux, with the -mpopcnt flag to be sure to use the CPU's POPCNT instruction. My test program counted the # of integers in the interval [0, 2^31) that were a power of two.At first I thought that
i && !(i & (i - 1)
was 10% faster, even though I verified that POPCNT was used in the disassembly where I used__builtin_popcount
.However, I realized that I had included an if statement, and branch prediction was probably doing better on the bit twiddling version. I removed the if and POPCNT ended up faster, as expected.
Results:
Intel(R) Core(TM) i7-4771 CPU max 3.90GHz
AMD Ryzen Threadripper 2950X 16-Core Processor max 3.50GHz
Note that here the Intel CPU seems slightly slower than AMD with the bit twiddling, but has a much faster POPCNT; the AMD POPCNT doesn't provide as much of a boost.
popcnt_test.c:
Run tests: