线性时间复杂度递推求 0 到 2的幂次-1 的二进制翻转

如何定义一个数的二进制翻转(后的数)?
也就是

\[reverse(\quad (\dots x_3x_2x_1x_0)_2 \quad) = \; ? \]


直观地理解, 就是把一个数的二进制表示的最后一位和第一位交换、倒数第二位和第二位交换……
但是就如上面的例子, 知道了一个数二进制表示的最后一位, 那么这个数的二进制表示下的第一位是什么?不规定第一位, 翻转后的数是确定且唯一的吗?

显然平常求一个数的二进制翻转, 是要求一个具体的数。
要求具体的数, 就要规定一个数的 “二进制位数”, 意即这个数在二进制表示下的第一位是从右到左第几位。


本文介绍的递推方法, 可以求二进制位数一定时的所有数的二进制翻转(显然有限的二进制位数只能表示有限个数, 即 \(x\) 位二进制只能表示 \([0, 2^x-1]\)\(2^x\) 个数)。

(由于不是我想出来的所以只给方法啦qwq)

具体方法是将一个二进制数 \((x_n \dots x_0)_2\) 看作 \((x_n \dots x_1)_2\)\((x_0)_2\)
首尾相接拼起来的两个数, 于是

\[reverse(\quad (x_n \dots x_0)_2 \quad) \]

\[= x_0 接上 \; reverse(\quad (x_n \dots x_1)_2 \quad) \]

(下面式子里的 \(reverse(\quad (x_n \dots x_1)_2 \quad)\) 是在二进制位数为 \(n\) 意义下的翻转)

但是递推到 \((x_n \dots x_0)_2\) 时, 手上只有 \((0\:x_n \dots x_1)_2\)
\(即(x_n \dots x_1)_2\)在二进制位数为 \(n+1\) 意义下的表示)
的二进制翻转, 怎么办?

其实操作一番就是了, 具体看代码就好啦。

最后一点就是由于 \(x_0\) 是二进制位数为 \(n\) 意义下的最后一位, 所以其必定被翻转到二进制位数为 \(n\) 意义下的第一位(帮助理解代码)。

for(int i=0; i<lim; ++i) { // lim是二的幂次
//这段代码是求 0 ~ lim-1 在二进制位数为 log_2(lim)-1 意义下的二进制翻转
//其中, 数 i 的翻转被储存在 re[i] 里
	re[i] = (re[i>>1]>>1)|((i&1)?lim>>1:0);
}
posted @ 2020-04-25 20:16  xwmwr  阅读(185)  评论(0编辑  收藏  举报