2的非负整数次幂的判断方法、证明以及一些思考

本文探讨如何判断一个整数是否为2的非负整数次幂,提出通过位操作`X & (X - 1) == 0`进行判断,并详细证明了其充分性和必要性。这种方法具有接近原子操作的高效性,揭示了二进制计数的红利。
部署运行你感兴趣的模型镜像

问题:

如何判断一个给定的整数是否为2的非负整数次幂?

通常的解法或者需要O(LOGN)的时间开销;或者通过时间换空间策略,建立一个LOG(MAX)大小的hash table解决限定范围内数据的判断问题。我们需要意识到的是,后者事实上是把运行时的运算开销前置到了编译期,总的计算成本是一样的。

前几天在学习极客时间上吴咏炜老师的《现代C++实战30讲》课程(顺便推荐吴咏炜老师的这门C++课程,需要有C++开发经验)时,发现吴老师在范例代码中展示了一种非常巧妙的算法:

 

X & (X - 1) == 0

 

思考了一下,发现这个算法非常优美且高效。其几乎接近原子操作,和其他算法O(LogN)的时间开销比起来判若云泥。以至于我甚至一度怀疑这两个命题的等价性。遂尝试如下证明:

 

证明 ”X是2的非负整数次幂“ 与 ”X & (X - 1) == 0“ 是等价的;

 

充分性证明:

已知X是2的正整数次幂;可一般性假设X的二进制可表达为m位上1,从m-1位到1位共m-1个0;任取Y < X, 可知Y的二进制表达中,第m位必为0;显见X & Y == 0; 可推知X & (X - 1) == 0;

 

必要性证明:

使用反证法。已知X&(X-1) == 0; 假设X不是2的正整数次幂;

则X的二进制表达为m位上是1,从m-1位到1位至少还还存在一个1;一般性假设第n (1<= n <= m-1)(1<= n <= m-1)是1;则有:

 

X = 2^{m} + 2^{n};

 

易得:

 

X-1 = 2^m + (2^n - 1);

 

由于1 <= n <= m - 1,可得:

 

2 <= 2^n <= 2^{m-1};

 

进一步可得:

 

2 - 1 <= 2^n -1 <= 2^{m-1} - 1;

 

可推出:

 

2^m + 2 - 1 <= 2^m + 2^n -1 <= 2^m + 2^{m-1} - 1;

 

即:

 

2^m + 1 <= X - 1 <= 2^m + 2^{m-1} - 1;

 

X - 1二进制表达中第m位必定为1;可推出X & (X - 1) != 0;与已知矛盾,假设条件不成立;

 

等价性成立。证明完毕。

 

思考:

  1. 类似的高效等价操作还有:右移操作和➗2;

  2. 针对2的整数次幂存在这样高效的算法,可以看做是使用二进制计数的红利。其实对于任意X进制系统中,X的任意整数次幂的判断是同样高效的。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

# P8482 「HGOI-1」Number ## 题目背景 $\text{bh1234666}$ 正在学习乘法! ## 题目描述 $\text{bh1234666}$ 有一定数量的数字 $0 \sim 9$,现在他想让你寻找一种分配方案,将它们分成两个整数,使得他们的乘积 $p$ 最大。 由于 $\text{bh1234666}$ 不喜欢太大的数,所以你只需要输出**两个非负整数**,使它们的乘积**等于**最大乘积 $p$,但是这两个整数 $0 \sim 9$ 的数量不能等于给定的数量(任意一个数字数量不相等即可,**不考虑前导零**)。 $\text{bh1234666}$ 是很善良的,如果 $0 \sim 9$ 的数量等于给定的数量了,你依旧可以得到的一半的分。 ## 输入格式 第一行十个整数 $c_0,c_1,\cdots c_9$,分别表示 $0 \sim 9$ 的个数。 ## 输出格式 共两行每行一个非负整数,分别表示你给出的两个非负整数。 ## 输入输出样例 #1 ### 输入 #1 ``` 1 2 3 2 1 1 2 1 2 1 ``` ### 输出 #1 ``` 13949030 620572547 ``` ## 说明/提示 #### 样例解释 最大可能乘积为 $97643210 \times 88653221=13949030 \times 620572547=8656385075279410$。 若输出 $97643210 \times 88653221$ 则只能得到一半的分,因为 $0\sim 9$ 出现的次数与给定的相同。 #### 数据范围及约定 本题采用**捆绑测试**,共有 $5$ 个 $\text{subtask}$,最终分数为所有 $\text{subtask}$ 分数之和。 $$ \def\arraystretch{1.5} \begin{array}{|c|c|c|}\hline \textbf{Task} & \textbf{Score} & \sum c_i\le \cr\hline 1 & 10 & 20 \cr\hline 2 & 20 & 100 \cr\hline 3 & 20 & 5000 \cr\hline 4 & 20 & 10^6 \cr\hline 5 & 30 & 10^7 \cr\hline \end{array} $$ 对于 $100\%$ 的数据,保证 $1 \le c_i$,$\sum c_i \le 10^7$。 #### 说明 本题有 $\text{spj}$,两数乘积正确得一半的分,数量与给出的不同且乘积正确得全部分数。故每一 $\text{subtask}$ 的得分为其中所有数据点得分的**最小值**。
07-09
在这道题目中,有 >首先,我们将上面这个式子列出其中的两项。 $$\begin{cases} a_i = a_i \oplus a_{(i + 1 )\mod n} \\ a_{i+1} = a_{i+1} \oplus a_{(i + 2 )\mod n} \end{cases}$$ 我们假定 $i + 2< n $,于是将 $\bmod$ 操作去掉。 将第一条式子异或第二条式子,有: $$ a_i \oplus a_{i+1} = a_i \oplus a_{i + 1} \oplus a_{i+1} \oplus a_{i+2} = a_i \oplus a_{i+2}$$ 题面如下: # CF1848F Vika and Wiki ## 题目描述 最近,Vika 正在研究她最喜欢的互联网资源——Wikipedia。 在 Wikipedia 的广阔天地中,她了解到了一种有趣的数学运算——按位异或(bitwise XOR),记作 $ \oplus $。 Vika 开始研究这种神秘运算的性质。为此,她取了一个由 $ n $ 个非负整数组成的数组 $ a $,并对其所有元素同时进行如下操作:$ a_i = a_i \oplus a_{(i+1) \bmod n} $。这里 $ x \bmod y $ 表示 $ x $ 除以 $ y $ 的余数。数组的下标从 $ 0 $ 开始。 由于仅进行一次上述操作还不足以彻底研究,Vika 会不断重复该操作,直到数组 $ a $ 变为全零。 请你判断,最少需要进行多少次上述操作,才能使数组 $ a $ 的所有元素都变为零。如果永远无法变为全零,则输出 $ -1 $。 ## 输入格式 第一行包含一个整数 $ n $($ 1 \le n \le 2^{20} $),表示数组 $ a $ 的长度。 保证 $ n $ 可以表示为 $ 2^k $,其中 $ k $ 是某个整数($ 0 \le k \le 20 $)。 第二行包含 $ n $ 个整数 $ a_0, a_1, a_2, \dots, a_{n-1} $($ 0 \le a_i \le 10^9 $),表示数组 $ a $ 的元素。 ## 输出格式 输出一个整数,表示使数组 $ a $ 的所有元素都变为零所需的最小操作次数。如果数组 $ a $ 永远无法变为全零,则输出 $ -1 $。 ## 输入输出样例 #1 ### 输入 #1 ``` 4 1 2 1 2 ``` ### 输出 #1 ``` 2 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 2 0 0 ``` ### 输出 #2 ``` 0 ``` ## 输入输出样例 #3 ### 输入 #3 ``` 1 14 ``` ### 输出 #3 ``` 1 ``` ## 输入输出样例 #4 ### 输入 #4 ``` 8 0 1 2 3 4 5 6 7 ``` ### 输出 #4 ``` 5 ``` ## 说明/提示 在第一个样例中,经过一次操作后,数组 $ a $ 变为 $ [3, 3, 3, 3] $。再进行一次操作后,数组变为 $ [0, 0, 0, 0] $。 在第二个样例中,数组 $ a $ 一开始就是全零。 在第三个样例中,经过一次操作后,数组 $ a $ 变为 $ [0] $。 那么请问,a_i异或a_i +1可以等于a_i异或a_i+3吗
最新发布
10-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值