Python int.bit_count()徹底解説:2進数の1の数を高速カウントする方法

 

int.bit_count()とは

int.bit_count()は、Python 3.10で導入されたメソッドで、整数の2進表現における1のビット数(ハミング重み)を効率的にカウントします。

# 基本的な使用例
numbers = [5, 7, 15, 255]

for num in numbers:
    binary = bin(num)
    count = num.bit_count()
    print(f"{num:3d}: {binary:>10s} → 1の数: {count}")

bit_count()の基本動作

正の整数での動作

# 様々な正の整数での例
test_numbers = [1, 2, 3, 4, 7, 8, 15, 16, 31, 255]

print("正の整数でのbit_count():")
print("数値  | 2進数     | 1の数")
print("------|-----------|------")

for num in test_numbers:
    binary_str = bin(num)[2:]  # '0b'プレフィックスを除去
    count = num.bit_count()
    print(f"{num:5d} | {binary_str:>9s} | {count:5d}")

ゼロでの動作

# ゼロの場合
zero = 0
print(f"0の2進数: {bin(zero)}")
print(f"0のbit_count(): {zero.bit_count()}")

大きな数での動作

# 大きな数での例
large_numbers = [1023, 1024, 2047, 2**20 - 1, 2**32 - 1]

print("大きな数でのbit_count():")
for num in large_numbers:
    binary_len = len(bin(num)) - 2  # '0b'を除いた桁数
    count = num.bit_count()
    print(f"{num:>10d}: {binary_len:2d}桁, 1の数: {count:2d}")

負の数でのbit_count()

負の数の扱い

# 負の数でのbit_count()
negative_numbers = [-1, -2, -3, -4, -7, -8]

print("負の数でのbit_count():")
print("数値 | bit_count() | 説明")
print("-----|-------------|-----")

for num in negative_numbers:
    try:
        count = num.bit_count()
        print(f"{num:4d} | {count:11d} | 無限に続く1のため")
    except ValueError as e:
        print(f"{num:4d} | エラー        | {e}")

負の数の代替処理方法

def bit_count_negative_safe(n):
    """負の数に対応したbit_count"""
    if n >= 0:
        return n.bit_count()
    else:
        # 負の数の場合は絶対値のbit_countを返す
        return abs(n).bit_count()

def bit_count_twos_complement(n, bit_width=32):
    """2の補数表現でのbit_count"""
    if n >= 0:
        return n.bit_count()
    else:
        # 指定ビット幅での2の補数表現
        mask = (1 << bit_width) - 1
        twos_complement = (~abs(n) + 1) & mask
        return bin(twos_complement).count('1')

# テスト
test_vals = [5, -5, 7, -7]
for val in test_vals:
    safe_count = bit_count_negative_safe(val)
    twos_count = bit_count_twos_complement(val, 8)
    print(f"{val:3d}: safe={safe_count}, 2's complement(8bit)={twos_count}")

bit_count()の実装パフォーマンス

他の実装方法との比較

def bit_count_manual(n):
    """手動でのbit_count実装"""
    if n < 0:
        raise ValueError("負の数は未対応")
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count

def bit_count_bin_string(n):
    """文字列変換による実装"""
    if n < 0:
        raise ValueError("負の数は未対応")
    return bin(n).count('1')

def bit_count_brian_kernighan(n):
    """Brian Kernighanのアルゴリズム"""
    if n < 0:
        raise ValueError("負の数は未対応")
    count = 0
    while n:
        n &= n - 1  # 最下位の1ビットを除去
        count += 1
    return count

# 正確性の検証
test_num = 12345
methods = [
    ("built-in bit_count", lambda x: x.bit_count()),
    ("manual", bit_count_manual),
    ("bin string", bit_count_bin_string),
    ("Brian Kernighan", bit_count_brian_kernighan)
]

print(f"数値 {test_num} ({bin(test_num)}) の結果比較:")
for name, func in methods:
    result = func(test_num)
    print(f"{name:20s}: {result}")

パフォーマンス測定

import time

def benchmark_bit_count_methods():
    """bit_count実装方法の性能比較"""
    test_numbers = list(range(1, 10001))
    iterations = 100
    
    methods = [
        ("int.bit_count()", lambda nums: [n.bit_count() for n in nums]),
        ("bin().count('1')", lambda nums: [bin(n).count('1') for n in nums]),
        ("manual loop", lambda nums: [bit_count_manual(n) for n in nums])
    ]
    
    print("パフォーマンス比較結果:")
    for name, method in methods:
        start = time.time()
        for _ in range(iterations):
            results = method(test_numbers)
        elapsed = time.time() - start
        print(f"{name:20s}: {elapsed:.6f}秒")

benchmark_bit_count_methods()

実用的な応用例

ハミング距離の計算

def hamming_distance(a, b):
    """2つの整数間のハミング距離"""
    return (a ^ b).bit_count()

# ハミング距離の例
pairs = [(5, 3), (15, 7), (255, 127), (1024, 512)]

print("ハミング距離の計算:")
for a, b in pairs:
    distance = hamming_distance(a, b)
    xor_result = a ^ b
    print(f"{a:4d} XOR {b:4d} = {xor_result:4d} ({bin(xor_result):>10s}) → 距離: {distance}")

人口カウント(Population Count)

def analyze_bit_patterns(numbers):
    """ビットパターンの分析"""
    results = []
    for num in numbers:
        bit_count = num.bit_count()
        bit_length = num.bit_length() if num > 0 else 1
        density = bit_count / bit_length if bit_length > 0 else 0
        
        results.append({
            'number': num,
            'binary': bin(num),
            'bit_count': bit_count,
            'bit_length': bit_length,
            'density': density
        })
    
    return results

# ビットパターン分析の実行
test_nums = [7, 15, 31, 63, 85, 170, 255]
analysis = analyze_bit_patterns(test_nums)

print("ビットパターン分析:")
print("数値  | 2進数      | 1の数 | 桁数 | 密度")
print("------|------------|-------|------|------")

for result in analysis:
    print(f"{result['number']:5d} | {result['binary']:>10s} | {result['bit_count']:5d} | {result['bit_length']:4d} | {result['density']:.3f}")

チェックサムとしての活用

def simple_checksum(data):
    """ビットカウントを使った簡単なチェックサム"""
    checksum = 0
    for byte_val in data:
        checksum += byte_val.bit_count()
    return checksum % 256

def verify_data_integrity(original_data, received_data):
    """データ整合性の簡易チェック"""
    original_checksum = simple_checksum(original_data)
    received_checksum = simple_checksum(received_data)
    
    return original_checksum == received_checksum, original_checksum, received_checksum

# データ整合性テスト
original = [0xFF, 0xAA, 0x55, 0x00, 0x0F]
received_ok = [0xFF, 0xAA, 0x55, 0x00, 0x0F]  # 正常
received_ng = [0xFF, 0xAA, 0x54, 0x00, 0x0F]  # 1ビット変化

print("データ整合性チェック:")
is_ok, orig_sum, recv_sum = verify_data_integrity(original, received_ok)
print(f"正常データ: 整合性={is_ok}, チェックサム={orig_sum}=={recv_sum}")

is_ok, orig_sum, recv_sum = verify_data_integrity(original, received_ng)
print(f"破損データ: 整合性={is_ok}, チェックサム={orig_sum}!={recv_sum}")

ビット操作での組み合わせ

ビットマスクとの組み合わせ

def analyze_permissions(permission_bits):
    """権限ビットの解析"""
    # 権限の定義
    READ = 0b100     # 4
    WRITE = 0b010    # 2  
    EXECUTE = 0b001  # 1
    
    permissions = []
    if permission_bits & READ:
        permissions.append('READ')
    if permission_bits & WRITE:
        permissions.append('WRITE')
    if permission_bits & EXECUTE:
        permissions.append('EXECUTE')
    
    active_bits = permission_bits.bit_count()
    
    return {
        'permissions': permissions,
        'binary': bin(permission_bits),
        'active_count': active_bits,
        'total_possible': 3
    }

# 権限パターンの分析
permission_patterns = [0b000, 0b001, 0b010, 0b100, 0b111, 0b101]

print("権限ビット分析:")
for pattern in permission_patterns:
    analysis = analyze_permissions(pattern)
    perms = ', '.join(analysis['permissions']) if analysis['permissions'] else 'None'
    print(f"{analysis['binary']:>5s}: {perms:20s} (アクティブ: {analysis['active_count']}/3)")

サブセット生成での活用

def generate_subsets_by_bit_count(items, target_count):
    """指定されたビット数のサブセットを生成"""
    n = len(items)
    subsets = []
    
    # 0から2^n-1まで全ての組み合わせを確認
    for i in range(2**n):
        if i.bit_count() == target_count:
            subset = [items[j] for j in range(n) if i & (1 << j)]
            subsets.append(subset)
    
    return subsets

# サブセット生成の例
fruits = ['apple', 'banana', 'cherry', 'date']
print(f"フルーツリスト: {fruits}")

for count in range(len(fruits) + 1):
    subsets = generate_subsets_by_bit_count(fruits, count)
    print(f"\n{count}個選ぶ組み合わせ ({len(subsets)}通り):")
    for subset in subsets:
        print(f"  {subset}")

バイナリファイル解析での活用

ファイルの0/1ビット分布解析

def analyze_file_bit_distribution(file_path):
    """ファイルのビット分布を解析"""
    try:
        with open(file_path, 'rb') as f:
            data = f.read()
        
        total_bits = len(data) * 8
        one_bits = sum(byte.bit_count() for byte in data)
        zero_bits = total_bits - one_bits
        
        return {
            'file_size_bytes': len(data),
            'total_bits': total_bits,
            'one_bits': one_bits,
            'zero_bits': zero_bits,
            'one_ratio': one_bits / total_bits if total_bits > 0 else 0,
            'entropy_estimate': min(one_bits, zero_bits) / total_bits * 2 if total_bits > 0 else 0
        }
    
    except FileNotFoundError:
        return None

# サンプルデータでのテスト
import tempfile
import os

# テスト用ファイルを作成
test_data = bytes([0xFF, 0x00, 0xAA, 0x55, 0x0F, 0xF0])
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
    temp_file.write(test_data)
    temp_path = temp_file.name

# ファイル解析
analysis = analyze_file_bit_distribution(temp_path)
if analysis:
    print("ファイルビット分布解析:")
    print(f"  ファイルサイズ: {analysis['file_size_bytes']} bytes")
    print(f"  総ビット数: {analysis['total_bits']}")
    print(f"  1ビット数: {analysis['one_bits']}")
    print(f"  0ビット数: {analysis['zero_bits']}")
    print(f"  1の割合: {analysis['one_ratio']:.3f}")
    print(f"  エントロピー推定: {analysis['entropy_estimate']:.3f}")

# テンポラリファイルを削除
os.unlink(temp_path)

まとめ

int.bit_count()は、Python 3.10で導入された効率的な1ビットカウント機能で、従来の文字列変換や手動ループよりも高速に動作します。ハミング距離計算、チェックサム生成、権限管理、データ解析など、様々な場面でビット操作と組み合わせて活用できます。負の数では無限に続く1が存在するため使用できませんが、適切な前処理により対応可能です。効率的なビット演算処理において、bit_count()は重要なツールとなります。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座