🐜

【部分集合全列挙】~ bit全探索 ~

に公開2

はじめに

今回は部分集合の全列挙について考えます!
競技プログラミングで出た時に、実装を忘れることがあるので改めて確認します。便利なライブラリがあるので、基礎を理解した上で省エネ実装もしてみます!

やりたいこと

配列を与えられた時に、全ての部分集合を列挙したい。
それぞれの要素に対して「選ぶ」と「選ばない」の2通りがあるので、全体としては2^n通りになります。

具体例

li = [1,2,3]
n = len(li) # n = 3

要素数3の配列を用意しました。
これを使って実装を考えてみます!

実装(手書き)

L = []
for bit in range(1 << n):
    li = []
    for i in range(n):
        if bit & (1 << i): li.append(1)
        else: li.append(0)
    L.append(li)
print(L)

出力

[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]

bit全探索を実装しています。これは、数を2進数と見てAND演算を考えるものです。
ex) 1 -> 001, 2 -> 010, 3 -> 011, 4 -> 100, ...
こうしていくと、綺麗に全パターンの2^n通りを挙げることができます。

実装(ライブラリ)

from itertools import product

L = list(product([0,1],repeat=3))
print(L)

出力

[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]

ライブラリを使用して実装しています。
直積を考えることで列挙ができます。

最後に

全探索をするための基本的なbit全探索を改めて確認しました。
個人的に覚えづらかったので、何のために何をしているか、を整理することができてよかったです。

最後まで読んでいただきありがとうございました!

参考文献

https://docs.python.org/ja/3.13/library/itertools.html

Discussion

shiracamusshiracamus

内包表記の書き方も覚えると便利ですよ。

L = [[(bit >> i) & 1 for i in range(n)[::-1]] for bit in range(1 << n)]
print(L)
2進数文字列処理
L = [[*map(int, f'{bit:0{n}b}')] for bit in range(1 << n)]
print(L)
1