x = ~x;
for (j = 1; j <= N/2; j *= 2) x &= (x >> j);
x &= (x >> (N - j + 1));
for (j = 1; j <= N/2; j *= 2) x |= (x << j);
x |= (x << (N - j + 1));
x = ~x;
与 R.. 的解决方案相同的想法,但有点优化。
为了进一步优化,可以消除第二个循环:
t = ~x;
m = x & (t << 1);
for (j = 1; j <= N/2; j *= 2) t &= (t >> j);
t &= (t >> (N - j + 1));
t |= ((m - t) & ~m);
x = ~t;
这里唯一剩余的循环移走位组(与之前的变体完全相同),但不是第二个循环,而是使用简单的按位技巧来恢复比 N 长的组。
示例(N = 4):
input string 110000100000011
inverted one 001111011111100
loop iter. 1 000111001111100
loop iter. 2 000001000011100
one more iter 000000000001100
第一次循环迭代工作正常,因为每个位组前面至少有一个零位。因此,我们在每个位组之前至少有两个零位。因此,可以在第二次循环迭代中一次移动两位。出于同样的原因,第三次循环迭代可能一次移动 4 位,等等。但是这个例子不需要移动大于 2 位。由于循环已经将位组移动了 3 位,我们必须将它们再移动 N-3=1 位(这由循环后的下一行完成)。
现在较小的位组消失了,但较大的位组由一对位表示。为了重建剩余的组,可以使用第二个循环:
starting with 000000000001100
loop iter. 1 000000000011100
loop iter. 2 000000001111100
one more iter 000000011111100
result 111111100000011
或者代替第二个循环,我们可以使用按位技巧:
m 010000100000000
t 000000000001100
m-t 010000011110100
(m-t) & ~m 000000011110100
t|((m-t)&~m) 000000011111100
result 111111100000011
m
标志着每组的开始。m-t
恢复所有移出的位。下一个操作清除 的未使用位m
。还需要一项操作来将恢复的位与移位循环之后剩余的位组合。
基准测试结果(AMD K8,GCC 4.6.3 -O2),秒:
N one_loop two_loops unoptimized
1 3.9 4.2 3.3
2 4.6 6.2 5.2
3 4.6 6.2 7.1
4 5.6 7.9 8.9
5 5.6 7.9 11.3
6 5.6 7.9 13.3
15 6.7 10.0 46.6