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..によるソリューションと同じアイデアですが、少し最適化されています。
さらに最適化するには、2 番目のループを削除することができます。
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;
ここでは、残りのループのみがビット グループをシフトします (前のバリアントとまったく同じ) が、2 番目のループの代わりに、単純なビット単位のトリックを使用して、N よりも長いグループを復元します。
例 (N = 4):
input string 110000100000011
inverted one 001111011111100
loop iter. 1 000111001111100
loop iter. 2 000001000011100
one more iter 000000000001100
各ビット グループの前に少なくとも 1 つのゼロ ビットがあるため、最初のループ反復は適切に機能します。その結果、各ビット グループの前に少なくとも 2 つのゼロ ビットがあります。そのため、2 回目のループ反復で一度に 2 ビットずつシフトすることができます。同じ理由で、3 番目のループ反復は一度に 4 ビットずつシフトする場合があります。ただし、この例では 2 ビットを超えるシフトは必要ありません。ループは既にビット グループを 3 ビット シフトしているので、それらをさらに N-3=1 ビットだけシフトする必要があります (これはループの後の次の行で行われます)。
小さい方のビット群はなくなりましたが、大きい方は一対のビットで表されます。残りのグループを再構築するには、2 番目のループを使用できます。
starting with 000000000001100
loop iter. 1 000000000011100
loop iter. 2 000000001111100
one more iter 000000011111100
result 111111100000011
または、2 番目のループの代わりに、ビット単位のトリックを使用することもできます。
m 010000100000000
t 000000000001100
m-t 010000011110100
(m-t) & ~m 000000011110100
t|((m-t)&~m) 000000011111100
result 111111100000011
m
各グループの始まりを示します。m-t
シフトアウトされたすべてのビットを復元します。次の操作で の未使用ビットがクリアされますm
。復元されたビットとシフト ループの後に残っているビットを結合するには、もう 1 つの操作が必要です。
ベンチマーク結果 (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