上周,在撰写我以前的文章时 ,我遇到了一个有趣的逆整数算法。 例如,它需要10100011
并将其转换为11000101
。 该算法的优点在于它是在O(log n)迭代中完成的。 这是代码:
是。 9行,包括签名和右括号。 让我们看看它是如何工作的。
下一部分将逐行研究代码。 如果您对技术无聊,可以随意跳到最后的动画 。
更新: O(log n)运行时间复杂度仅适用于n≤机器字大小,今天通常为64位。 感谢所有引起我注意的人。
技术:递归块交换
该算法通过递归交换相邻的位块来工作,从而在每次迭代时减小块大小。 首先交换2个n / 2位的块,然后交换4个n / 4位的块,8个n / 8位的块,依此类推,最后交换n个1位的块。 在每次迭代中,所有相邻块对都并行交换。 结果是倒数。 为了并行交换块,算法使用按位 运算和位掩码 。
注意 :原始代码使用32位输入。 我们将使用8位整数使事情更容易理解。
交换相邻的位块
并行交换块是通过特制的位掩码完成的。
对于块大小 s
,位掩码mask
由s
零和s
个交替的块组成。 例如,对于s = 4
, mask == 00001111
。 对于s = 2
, mask == 00110011
。
该行使用掩码来并行交换所有对块:
num = ((num >> s) & mask) | ((num << s) & ~mask);
-
(num >> s) & mask)
将偶数块s
位置向右移动,并使用该掩码清除奇数块 。 -
(num << s) & ~mask
将奇数块s
位置向左移动,并使用掩码的按位NOT清除偶数块 。 - 按位或用于将这些结果加到交换的数字中。
非常简单,不是吗?
![](https://i-blog.csdnimg.cn/blog_migrate/ab49c5a8ca13746cc7f40034a3595f32.png)
创建遮罩
另一个很酷的技巧是如何创建mask
。 mask
由大小为s
的0和1的交替块组成。 掩码以全1的数字开头,并在循环的第一行进行更新:
while ((s >>= 1) > 0) {
mask ^= (mask << s);
...
这是在s
减半后立即发生的,因此s
是mask
块大小的一半。 在第一次迭代中, mask == 11111111
和s == 4
。 通过将自身与另一个自身的副本进行XOR运算来对掩码进行左移s
位:
mask =
11111111 XOR // mask
11110000 // mask << s
= 00001111
如果两个位彼此不相等 ,则两个位的XOR为1。 在掩码更新的每次迭代中,所有块都向左移动其大小的一半。 当一个块与前一个掩码进行异或时,该块的一半与零重叠,另一半与一重叠。 这将在新蒙版中创建2个块,每个块的大小均为先前块的一半。 这是一个例子:
0000000011111111 // original mask, 8-bit blocks
0000111111110000 // mask shifted left by block-size/2
0000111100001111 // XORed: new mask, 4-bit blocks
绑在一起
这是一个简短的动画,展示了运行中的算法:
![](https://i-blog.csdnimg.cn/blog_migrate/3ca9343eeb02e733b8f790623f8a3a92.png)
令人惊讶的是,在9行代码中可以找到多少深度。 如果您想让我探索一段有趣的代码,请在下面留下答复 !
From: https://hackernoon.com/reversing-an-n-bit-number-in-o-log-n-time-9bf69363d452