def power_of_two?(n)
n & (n-1) == 0
end
此方法检查给定数字n
是否为2的幂。
这是如何运作的?我不明白&
答案 0 :(得分:2)
&
称为按位AND运算符。
The AND
operator逐位遍历两个提供的整数的二进制表示。如果两个整数中相同位置的位为1,则结果整数将该位设置为1.如果不是,该位将设置为0:
(a = 18).to_s(2) #=> "10010"
(b = 20).to_s(2) #=> "10100"
(a & b).to_s(2) #=> "10000"
如果数字已经是2的幂,那么少一个将导致只设置了低阶位的二进制数。使用&
将无能为力。
0100 & (0100 - 1)
- > (0100 & 0011)
- > 0000
要理解它,请遵循“How does this bitwise operation check for a power of 2?”。
IRB的例子:
>> 4.to_s(2)
=> "100"
>> 3.to_s(2)
=> "11"
>> 4 & 3
=> 0
>>
这就是为什么你可以说4
是 2 号的力量。
答案 1 :(得分:1)
“&”是一个有点“AND”(见http://calleerlandsson.com/2014/02/06/rubys-bitwise-operators/)运算符。它比较两个数字,如以下示例中所述:
假设n = 4(这是2的幂)。这意味着n-1 = 3。在二进制文件中(我用引号中的1和0编写,如“1101011101”,所以我们可以看到这些位)我们有n =“100”和n-1 =“011”。
这两个数字的逐位AND是0 =“000”(在下面,每列只包含一个1,从不包含两个1)
100 <-- this is n, n=4
011 <-- this is n-1, n-1=3
---
000 <-- this is n & (n-1)
作为另一个例子,现在假设n = 14(不是2的幂),因此n-1 = 13。在那种情况下,n =“1110”并且n-1 =“1101”,并且我们有n&amp; (n-1)= 12
1110 <-- this is n, n=14
1101 <-- this is n-1, n-1=13
----
1100 <-- this is n & (n-1)
在上面的例子中,n和n-1的前两列都包含1,因此这些列的AND是1。
好吧,让我们考虑一个最后一个例子,其中n再次是2的幂(如果不是为什么“poweroftwo?”按原样写的话,这应该是非常明确的。假设n = 16(这是一个权力)两个)。
假设n = 16(这是2的幂)。这意味着n-1 = 15因此我们有n =“10000”和n-1 =“01111”。
这两个数字的逐位AND是0 =“00000”(在下面,每列只包含一个1,从不包含两个1)
10000 <-- this is n, n=16
01111 <-- this is n-1, n-1=15
---
00000 <-- this is n & (n-1)
警告:在n = 0的特殊情况下,函数“power_of_two?”即使n = 0不是2的幂,它也会返回True。这是因为0表示为全零的位串,任何与零相关的任何值都为零。
所以,一般来说,功能“power_of_two?”当且仅当n是2的幂或n为零时才返回True。上面的例子只说明了这个事实,他们并没有证明这一点......但事实并非如此。
答案 2 :(得分:1)
从二进制数减去1的过程是从最低有效位开始:
0
- 将其转为1
并继续下一个有效位1
- 将其转为0
并停止。这意味着如果数字中有多个1
数字,则不会切换所有数字(因为您在获得最高位之前停止了)。
我们说我们的号码1
中的第一个 n
位于i
位置。如果我们向右移动数字n
,我们将得到数字的一部分,当我们减少一个数字时没有改变,让我们称之为m
。如果我们改变数字n-1
,我们应该得到相同的数字m
,正是因为当我们减少一个时,它是没有改变的部分:
n >> i == m
(n - 1) >> i == m
将相同数量的两个数字向右移动也会向&
的结果移动相同的数量:
(n >> i) & ((n - 1) >> i) == 0 >> i
但是0 >> i
是0
,无论i
,所以:
(n >> i) & ((n - 1) >> i) == 0
让我们把m
放在我们知道的地方:
m & m == 0
但我们也知道:
m & m == m # for any m
所以m == 0
!
因此n & (n - 1) == 0
当且仅当数字1
中最多有一个n
位。
最多只有一个1
位的唯一数字是2
的所有(非负)幂(前导1
)和后面的非负数量的零),以及数字0
。
QED
答案 3 :(得分:1)
我们希望证明
n & (n-1) == 0
当且仅当n
具有2
的权力。
我们可以假设n
是一个大于1
的整数。 (事实上,我将使用这个假设来获得收缩。)
如果n
是2
的幂,则其二进制表示在位偏移处有1
p = log2(n)
和0
位于所有低位位j
,j < p
。此外,由于(n-1)+1 = n
,n-1
在所有位偏移1
,j
上必须0 <= j < p
。因此,
n & (n-1) == 0
如果n
不是2
和
n & m == 0
然后m != n-1
。我假设m = n-1
并且将获得收缩,从而完成证明。
n
最重要的位当然是1.由于n
不是2的幂,n
至少有一个等于{{1}的位}}。在这些1位中,考虑最高有效位1
处的那个。
由于j
,n & (n-1) == 0
必须在其二进制表示的n-1
位置0
。当我们将j
添加到1
时,为了使其n-1
,它必须在偏移n
处有1
,这意味着j
必须所有位位置都有n-1
&lt; 1&gt; 1
。此外,j
在所有比特位置都具有零&lt;添加(n-1)+1
后j
。但是从1
开始,只有n = (n-1)+1
,才能成为j == 0
。因此,为此,n & (n-1) == 0
最高有效位和最低有效位大多数都等于n
,所有其他位必须等于零。但是,由于1
,这意味着n = (n-1)+1
,因此n-1==0
,这是必然的矛盾。
(哇!这是一个更容易证明!)
答案 4 :(得分:0)
在幂为2的情况下,它采用值为1的单个位的二进制形式,后跟零。递减时的任何这样的值将采用1的运行形式,因此当使用按位时 - 并且由于它必然小于前者,它将掩盖它。 E.g。
0b1000&amp; (0b1000 - 1)= 0b1000&amp; 0b111 = 0
所以,任何东西(num-1)都可能变成,这里的关键是触及num的最高位,通过减少它,我们清除它。
另一方面,如果数字不是2的幂,则结果必须为非零。
背后的原因是操作总是可以在不触及最高位的情况下进行,因为在路上总会有一个非零位,所以至少最高位使它成为了掩码并将显示在结果中。