OR / XOR でフラグを操作する
OR / XOR でフラグを操作する
このレッスンで分かること
- どちらか一方でも
1なら結果は1です- 両者が異なるときだけ
1ですkビット目を立てるにはn | (1 << k)を計算します
OR / XOR でフラグを操作する とは
ビット OR でフラグを立て、XOR でフラグを反転させる手法を学び、複数フラグの統合を実装する。本レッスンでは、OR / XOR でフラグを操作する の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
前のレッスンでは AND を使って ビットを取り出す ことを学びました。今回は OR で ビットを立てる、XOR で ビットを反転させる という、ビット演算のもう 2 つの基本を学びます。3 つ揃えば、フラグ管理に必要な操作はだいたい何でもできます。
本レッスンの題材は「フラグ番号のリストを受け取り、対応するビットをすべて立てた整数を返す関数」です。具体的には、[0, 2, 3] を渡されたら 0b1101 = 13 を返します。
AND は取り出す、OR は立てる、XOR は反転する。この 3 つを覚えればフラグ管理は怖くない。
OR の真理値表
プレーンテキスト
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1どちらか一方でも 1 なら結果は 1 です。AND と違って、OR は「ビットを立てる」のに使います。
XOR の真理値表
プレーンテキスト
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0両者が異なるときだけ 1 です。同じビットを 2 回 XOR すると元に戻るという面白い性質があり、暗号やハッシュの基礎にも使われます。a ^ a = 0 a ^ 0 = a という 2 つの法則がとても重要です。
XOR は「同じなら 0、違うなら 1」。2 回かけると元に戻る性質が、暗号化の鍵にもなる。
フラグを立てる典型パターン
k ビット目を立てるには n | (1 << k) を計算します。
n = 0000、k = 2のとき:0000 | 0100 = 0100n = 0101、k = 1のとき:0101 | 0010 = 0111
すでに立っているビットを OR してももう一度立つだけで何も変化しません。これが「冪等」と呼ばれる性質です。
k ビット目を反転 (toggle) するには n ^ (1 << k) を使います。
n = 0101、k = 0のとき:0101 ^ 0001 = 0100(0 ビット目が消えた)n = 0100、k = 0のとき:0100 ^ 0001 = 0101(0 ビット目が立った)
Python での実装
Python
def flagsToInt(bits):
result = 0
for k in bits:
result = result | (1 << k)
return result受け取った bits リストの各要素 k について、1 << k のマスクと OR を取って積み上げます。たとえば [0, 2, 3] なら、0 | 1 = 1、1 | 4 = 5、5 | 8 = 13 で最終的に 13 です。
JavaScript での実装
JavaScript
function flagsToInt(bits) {
let result = 0;
for (const k of bits) {
result = result | (1 << k);
}
return result;
}ロジックは Python と同じです。
XOR の不思議な使い方
XOR には次のような面白い応用があります。a ^ b ^ b = a という性質を利用すると、temp 変数なしで 2 つの値を入れ替えられます。
Python
a = 5
b = 3
a = a ^ b # a = 6
b = a ^ b # b = 5
a = a ^ b # a = 3
# a と b が入れ替わった実務でこれを書くと可読性が落ちますが、XOR の性質を理解するうえで覚えておく価値があります。さらに、配列の中で 1 つだけ重複していない数を見つける ような問題でも、全要素を XOR で畳み込むと重複したものは消えて、独りぼっちの値だけが残るというテクニックがあります。
全要素を XOR で畳み込むと、
偶数回出現したものは消える奇数回のみ出現したものが残る。
よくある間違い
1 つ目は | と || の混同。| はビット演算、|| は論理演算で意味が全く違います。Python では or がブール演算、| がビット演算です。2 つ目は 演算子優先順位 の罠で、result | 1 << k のように書くと、<< の方が | より優先順位が高いので意図通りに動きますが、迷うくらいなら 必ず括弧 を書くべきです。3 つ目は 負のシフト量 1 << -1 です。Python では ValueError が送出され、JavaScript ではシフト量が下位 5 ビット (0〜31) に丸められるため 1 << 31 = -2147483648 という意図しない結果になります。C/C++ では未定義動作です。いずれにせよ負のシフトは避けましょう。
やってみよう
flagsToInt([0, 1, 2])を呼ぶと0b111 = 7が返ることを確かめる。これは UNIX のchmod 7(全権限) と同じ意味。13 ^ 0b1111 = 0010 = 2を計算して、XOR を NOT っぽく使えることを確認。マスク0b1111でビット幅を制限した範囲内の反転。- 与えられた
intの特定ビットkを 0 にする にはどう書く? ヒントn & ~(1 << k)を試す。 XORの数学的性質「結合律」「交換律」が成り立つことをa ^ b ^ cの順序を変えて検証する。
3 つの基本演算 (AND OR XOR) が揃ったので、次は ビット数を数える popcount に進みます。
よくある質問
Q. ビット演算は実務でいつ使いますか?
A. フラグ管理(permission の rwx)、画像処理(マスク)、ハッシュ計算の高速化などで使います。複数の真偽値を 1 つの整数にまとめると、メモリ削減と演算高速化の両方が得られます。例えば 8 個の権限を 1 byte で表せます。
Q. AND / OR / XOR の代表的な使い道は?
A. AND は「特定ビットの確認・抽出」(flags & PERM_READ)、OR は「ビットの立て上げ」(flags |= PERM_WRITE)、XOR は「ビット反転や暗号化」で使います。XOR は 2 回かけると元に戻る性質から、簡易暗号や両端ポインタの入れ替えなどに応用できます。
Q. シフト演算で割り算しても良いですか?
A. 正の整数の 2 のべき乗での除算なら x >> n と x / (1<<n) は同じ結果になります。ただし負の数では言語ごとに挙動が違う(算術シフトと論理シフトの区別)ため、可読性と安全性のために通常の演算子を使い、性能ボトルネックでのみシフトを検討してください。
次のレッスン
次は popcount で 1 のビット数を数える に進みましょう。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- OR / XOR の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. OR / XOR とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- bits は 0 以上の整数のリスト (同じ値が複数回含まれてもよい)
- ビット OR 演算 (|) を使うこと
- 空配列の場合は 0 を返すこと
入出力例
test-cases.txt
flagsToInt([0,2,3]) → 13
flagsToInt([0]) → 1
flagsToInt([0,1,2]) → 7
flagsToInt([3]) → 8
flagsToInt([1,1,2]) → 6
flagsToInt([10]) → 1024