NumPy配列でビット演算を極める!AND, OR, XOR, NOT, シフト操作を徹底解説


 

データ処理や低レベルの数値計算、特定のフラグ管理などにおいて、数値の**ビット単位での操作(ビット演算)**は非常に重要です。NumPyライブラリは、これらのビット演算を効率的に配列全体に適用できる機能を提供しています。この記事では、NumPy配列(ndarray)で利用できる論理ビット演算子(AND, OR, XOR, NOT)とビットシフト操作について、具体的なサンプルコードを交えながら詳しく解説します。


 

NumPyにおけるビット演算の基本

 

ビット演算は、数値を2進数として捉え、それぞれのビット(0か1)に対して論理操作やシフト操作を行います。NumPyでは、これらの操作が配列の各要素に対して一括で適用されるため、非常に高速です。

重要: ビット演算は主に整数型の数値に対して行われます。浮動小数点数に適用すると予期せぬ結果になることがあるため注意が必要です。


 

論理ビット演算子

 

NumPyでは、Pythonのビット演算子とほぼ同じ記号を使用して、要素ごとのビット演算を実行できます。

 

1. ビットごとのAND:& (AND)

 

&演算子は、対応するビットが両方とも1の場合にのみ結果のビットを1にします。

 

サンプルコード

 

Python
 
import numpy as np

arr1 = np.array([5, 6, 10], dtype=np.int32)  # 5(0101), 6(0110), 10(1010)
arr2 = np.array([3, 7, 12], dtype=np.int32)  # 3(0011), 7(0111), 12(1100)

# arr1 & arr2 (要素ごとのビットAND)
# 5 & 3 = 1 (0001)
# 6 & 7 = 6 (0110)
# 10 & 12 = 8 (1000)
result_and = arr1 & arr2
print("ビットANDの結果:", result_and)

 

出力例

 

ビットANDの結果: [1 6 8]

 

2. ビットごとのOR:| (OR)

 

|演算子は、対応するビットのどちらか一方、または両方が1の場合に結果のビットを1にします。

 

サンプルコード

 

Python
 
import numpy as np

arr1 = np.array([5, 6, 10], dtype=np.int32)
arr2 = np.array([3, 7, 12], dtype=np.int32)

# arr1 | arr2 (要素ごとのビットOR)
# 5 | 3 = 7 (0111)
# 6 | 7 = 7 (0111)
# 10 | 12 = 14 (1110)
result_or = arr1 | arr2
print("ビットORの結果:", result_or)

 

出力例

 

ビットORの結果: [ 7  7 14]

 

3. ビットごとのXOR:^ (XOR)

 

^演算子(排他的論理和)は、対応するビットが異なる場合(片方だけが1)に結果のビットを1にします。

 

サンプルコード

 

Python
 
import numpy as np

arr1 = np.array([5, 6, 10], dtype=np.int32)
arr2 = np.array([3, 7, 12], dtype=np.int32)

# arr1 ^ arr2 (要素ごとのビットXOR)
# 5 ^ 3 = 6 (0110)
# 6 ^ 7 = 1 (0001)
# 10 ^ 12 = 6 (0110)
result_xor = arr1 ^ arr2
print("ビットXORの結果:", result_xor)

 

出力例

 

ビットXORの結果: [6 1 6]

 

4. ビットごとのNOT:~ (NOT)

 

~演算子(ビット反転)は、各ビットを反転させます(01に、10に)。これは通常、符号付き整数では補数として解釈されます。

 

サンプルコード

 

Python
 
import numpy as np

arr = np.array([5, -5], dtype=np.int8) # int8で定義すると分かりやすい
# 5 (00000101) のビットNOTは、-6 (11111010) になることが多い(2の補数表現)

result_not = ~arr
print("ビットNOTの結果:", result_not)

 

出力例

 

ビットNOTの結果: [-6  4]

 

ビットシフト操作

 

ビットシフトは、数値のビットパターンを指定された数だけ左右に移動させる操作です。

 

1. 左シフト:<<

 

<<演算子は、ビットを指定された数だけ左にシフトします。これにより、元の数値は倍されます(はシフトするビット数)。

 

サンプルコード

 

Python
 
import numpy as np

arr = np.array([1, 2, 3], dtype=np.int32)

# 各要素を左に2ビットシフト
# 1 (0001) << 2 = 4 (0100)
# 2 (0010) << 2 = 8 (1000)
# 3 (0011) << 2 = 12 (1100)
result_left_shift = arr << 2
print("左シフトの結果:", result_left_shift)

 

出力例

 

左シフトの結果: [ 4  8 12]

 

2. 右シフト:>>

 

>>演算子は、ビットを指定された数だけ右にシフトします。これにより、元の数値はで除算されます(小数点以下は切り捨て)。

 

サンプルコード

 

Python
 
import numpy as np

arr = np.array([10, 15, 20], dtype=np.int32)

# 各要素を右に2ビットシフト
# 10 (1010) >> 2 = 2 (0010)
# 15 (1111) >> 2 = 3 (0011)
# 20 (10100) >> 2 = 5 (0101)
result_right_shift = arr >> 2
print("右シフトの結果:", result_right_shift)

 

出力例

 

右シフトの結果: [2 3 5]

 

まとめ

 

NumPyのビット演算機能は、配列内の整数データを効率的に操作するために非常に強力です。

  • 論理ビット演算子:

    • & (AND): 両方のビットが1の場合に1

    • | (OR): 少なくとも一方のビットが1の場合に1

    • ^ (XOR): ビットが異なる場合に1

    • ~ (NOT): ビットを反転(2の補数表現に注意)

  • ビットシフト操作:

    • <<: 左シフト(倍)

    • >>: 右シフト(で除算、切り捨て)

これらの操作は、フラグ管理、ビットマスク処理、データのエンコード/デコード、あるいは特定のアルゴリズムの実装など、多岐にわたる用途で活用されます。ビット単位での操作をマスターすることで、より低レベルでのデータ制御が可能になり、NumPyのパフォーマンスを最大限に引き出すことができるでしょう。ぜひ、あなたのNumPyワークフローに取り入れてみてください!