「マスクされた」ビットセットのインクリメント


81

私は現在、次の問題に遭遇したツリー列挙子を作成しているところです。

私はつまり、マスクされたビットセット、セットビットは、マスクのサブセットであり、すなわちビットセット、で探してい0000101たマスクで1010101。私が達成したいのはビットセットをインクリメントすることですが、マスクされたビットに関してのみです。この例では、結果はになります0010000。少し明確にするために、マスクされたビットのみを抽出します。つまり0011、それらをインクリメントして0100マスクビットに再度分配し、を与え0010000ます。

ビットスキャンとプレフィックスマスクの組み合わせを使用して手動で操作を実装する以外に、これを行う効率的な方法を誰かが見ていますか?

回答:


123

マスク以外のビットを1で埋めて、キャリーを伝播します。

// increments x on bits belonging to mask
x = ((x | ~mask) + 1) & mask;

11
それは素晴らしいトリックです...私が言ったほとんどの魔法はありません:)
ユージーンSh。

8
@EugeneSh。そうではないと決して信じないでください。
zch 2017年

24
OPは受け入れたのでおそらく重要ではありませんが、これにより非マスクビットがゼロになることに注意してください。それらが場合された他の場所で必要な、あなたは交換し、より注意する必要があるだろうx。おそらくx = (x & ~mask) | (((x | ~mask) + 1) & mask);
TripeHound 2017年

2
@TripeHound必要がなければ、ビットマスクを使用しても意味はありますか?
someonewithpc

1
@someonewithpc何を言おうとしているのかわからない。OPが隣接していないビットのセットをインクリメントする必要がある理由がわからないため、元の値の他のビットが重要かどうかもわかりません。たとえば、元の値が0101101(たとえば.1.1.0.、非マスクビットと0.0.1.1「カウンター」に)必要であるか 01110000.1.0.0保存中の新しい「カウンター」.1.1.0.)、または単に0010000許容できる場合です。この答え(そしておそらく私はチェックしていませんが他の答え)は後者を与えます。それが必要な場合、私のバージョンは前者を与えるはずです。
TripeHound 2017年

20

受け入れられた答えと比較して直感的ではありませんが、これは3つのステップでのみ機能します。

x = -(x ^ mask) & mask;

これは、zchによって提案されているように確認できます。

  -(x ^ mask)
= ~(x ^ mask) + 1  // assuming 2's complement
= (x ^ ~mask) + 1
= (x | ~mask) + 1  // since x and ~mask have disjoint set bits

そうすれば、受け入れられた答えと同等になります。


2
zchの答えはとても直感的で、彼の明確な説明のおかげですぐに正しいことがわかります。この答えの論理は何ですか?この式はどのように機能して、望ましい効果をもたらしますか?私は発見のプロセス、ここでの洞察の性質に興味があります。
FooF 2017年

-(x ^ mask) == (x | ~mask) + 1xがマスクのサブセットであることを証明し、それから私の答えを参照すれば、検証ははるかに簡単になると思います。
zch 2017年

5
-(x^mask) == ~((x ^ mask) - 1) == ~(x ^ mask) + 1 == (x ^ ~mask) + 1 == (x | ~mask) + 1。最後の方程式は、ビットセットが互いに素であるために成り立ち、他の方程式は常に真です(少なくとも2の補数では)。
zch 2017年

1
この答えを導き出すために私が取った手順に興味がある人は、このページを参照できます
nglee 2017年

5
おそらく、これらは同じものを最適化しないことを指摘する価値があります。これは、ビットをいじる人々に関連することがよくあります:godbolt.org/g/7VWXas-どちらが実際に短いかはコンパイラに依存しているようですが。どちらが速いか、または違いが大きいかどうかはわかりません。
Leushenko 2017年

7

反復の順序がそれほど重要ではなく、デクリメント操作でニーズを満たす場合は、次の2つの操作のみを使用できます。

から始めましょう

x = mask

で以前の値を取得します

x = (x - 1) & mask

x - 1partは、最後の非ゼロビットをゼロに変更し、重要度の低いすべてのビットを1に設定します。次に& maskパーツはそれらの間にマスクビットのみを残します。


2 ops、いいね。ただし、これは同じアプローチであり、1を実行するのではなく、0を介して借用を伝播するだけであると私は主張します。
zch 2017年

@zch、そうです、ありがとう。答えを言い換えます
DAle 2017年

xがすべての非マスクビットをクリアして開始する場合にのみ機能します。
Jasen 2017年

@ジェイセン、確かに。しかし、これらの非マスクビットを設定することは難しくありません。そして他の答えにも同様の問題があります。
DAle 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.