OR / XOR でフラグを操作する

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

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 = 0000k = 2 のとき: 0000 | 0100 = 0100
  • n = 0101k = 1 のとき: 0101 | 0010 = 0111

すでに立っているビットを OR してももう一度立つだけで何も変化しません。これが「冪等」と呼ばれる性質です。

k ビット目を反転 (toggle) するには n ^ (1 << k) を使います。

  • n = 0101k = 0 のとき: 0101 ^ 0001 = 0100 (0 ビット目が消えた)
  • n = 0100k = 0 のとき: 0100 ^ 0001 = 0101 (0 ビット目が立った)
diagram (will load when visible)

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 = 11 | 4 = 55 | 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 の特定ビット k0 にする にはどう書く? ヒント 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 つができることを確認しましょう。

  1. OR / XOR の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. OR / XOR とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. bits は 0 以上の整数のリスト (同じ値が複数回含まれてもよい)
  2. ビット OR 演算 (|) を使うこと
  3. 空配列の場合は 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

ヒント

main.py
main.py
学習モード

メモ

OR / XOR でフラグを操作する

⌘S で保存