itertools.product() / itertools.permutations() / itertools.combinations()
| 対応: | Python 2(2000) |
|---|
『itertools.product()』は複数のイテラブルの直積(デカルト積)を生成します。直積とは、2つのグループから1つずつ選ぶ全ての組み合わせのことです。例えば色3種×サイズ3種なら9通りになります。『itertools.permutations()』は順列(順序あり・重複なし)、『itertools.combinations()』は組み合わせ(順序なし・重複なし)を生成します。これらは結果をメモリに保持せず遅延評価で生成するため、大量の組み合わせも効率的に処理できます。
構文
import itertools # 直積(デカルト積) itertools.product(*iterables, repeat=1) # 順列(重複なし) itertools.permutations(iterable, r=None) # 組み合わせ(重複なし) itertools.combinations(iterable, r) # 組み合わせ(重複あり) itertools.combinations_with_replacement(iterable, r)
関数一覧
| 関数 | 概要 |
|---|---|
| product(*iterables, repeat=1) | 複数のイテラブルの直積をタプルで生成する。repeatで同一イテラブルを繰り返す。 |
| permutations(iterable, r) | r個の要素の順列をすべて生成する。rを省略すると全要素の順列。 |
| combinations(iterable, r) | r個の要素の組み合わせをすべて生成する(重複なし)。 |
| combinations_with_replacement(iterable, r) | r個の要素の組み合わせをすべて生成する(重複あり)。 |
サンプルコード
itertools_product.py
import itertools # product: 直積(すべての組み合わせ) grades = ['grade_A', 'grade_B', 'grade_C'] members = ['member_a', 'member_b'] result = list(itertools.product(grades, members)) print(result) # product: repeatで同一リストの直積(サイコロ2個) dice = list(itertools.product(range(1, 7), repeat=2)) print(len(dice)) print(dice[:3])
実行すると次のように出力されます。
python3 itertools_product.py
[('grade_A', 'member_a'), ('grade_A', 'member_b'), ('grade_B', 'member_a'), ('grade_B', 'member_b'), ('grade_C', 'member_a'), ('grade_C', 'member_b')]
36
[(1, 1), (1, 2), (1, 3)]
itertools_permutations.py
import itertools # permutations: 順列(順序あり・重複なし) members = ['member_c', 'member_d', 'member_e'] perms = list(itertools.permutations(members, 2)) print(perms) # permutations: 全要素の順列 all_perms = list(itertools.permutations([1, 2, 3])) print(len(all_perms))
実行すると次のように出力されます。
python3 itertools_permutations.py
[('member_c', 'member_d'), ('member_c', 'member_e'), ('member_d', 'member_c'), ('member_d', 'member_e'), ('member_e', 'member_c'), ('member_e', 'member_d')]
6
itertools_combinations.py
import itertools # combinations: 組み合わせ(順序なし・重複なし) combs = list(itertools.combinations([1, 2, 3, 4], 2)) print(combs) # combinations_with_replacement: 重複を許す組み合わせ combs_rep = list(itertools.combinations_with_replacement(['A', 'B', 'C'], 2)) print(combs_rep)
実行すると次のように出力されます。
python3 itertools_combinations.py
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
よくあるミス
よくあるミス1: product() の結果の件数を過小評価する
直積は要素数の掛け算になるため、入力が増えると件数が急増します。大きなリストを組み合わせる前に件数を確認する習慣をつけると安全です。
import itertools # NG: 要素数の多いリストの直積を全部 list() に変換しようとする grades = ['grade_A', 'grade_B', 'grade_C', 'grade_D', 'grade_E'] skills = ['skill_1', 'skill_2', 'skill_3', 'skill_4', 'skill_5'] targets = ['member_a', 'member_b', 'member_c', 'member_d', 'member_e'] # 5 × 5 × 5 = 125件はまだ許容範囲だが、repeat=4 では 5^4 = 625件、repeat=10 では約1000万件 result = list(itertools.product(range(100), repeat=5)) print(len(result))
mistake1_ok.py
import itertools
grades = ['grade_A', 'grade_B', 'grade_C']
members = ['member_a', 'member_b', 'member_c']
# OK: まず件数を確認してから処理する
count = sum(1 for _ in itertools.product(grades, members))
print(f"生成件数: {count}")
# 件数が許容範囲なら処理を進める
if count <= 1000:
result = list(itertools.product(grades, members))
print(result[:3])
実行すると次のように出力されます。
python3 mistake1_ok.py
生成件数: 9
[('grade_A', 'member_a'), ('grade_A', 'member_b'), ('grade_A', 'member_c')]
よくあるミス2: permutations と combinations の違いを混同する
permutations は順序あり(ABとBAは別)、combinations は順序なし(ABとBAは同じ)です。どちらを使うかは「順番が意味を持つか」で判断します。
mistake2_ng.py
import itertools
# チームの組み合わせを求めたいのに permutations を使っている
# NG: 順序が違うだけの重複が含まれる
team_members = ['member_a', 'member_b', 'member_c', 'member_d']
pairs = list(itertools.permutations(team_members, 2))
print(f"件数: {len(pairs)}")
print(pairs[:4])
実行すると次のように出力されます。
python3 mistake2_ng.py
件数: 12
[('member_a', 'member_b'), ('member_a', 'member_c'), ('member_a', 'member_d'), ('member_b', 'member_a')]
チームの組み合わせでは(member_a, member_b)と(member_b, member_a)は同じペアです。combinations を使うと重複なしで取得できます。
mistake2_ok.py
import itertools
# OK: チームの組み合わせには combinations を使う
team_members = ['member_a', 'member_b', 'member_c', 'member_d']
pairs = list(itertools.combinations(team_members, 2))
print(f"件数: {len(pairs)}")
print(pairs)
実行すると次のように出力されます。
python3 mistake2_ok.py
件数: 6
[('member_a', 'member_b'), ('member_a', 'member_c'), ('member_a', 'member_d'), ('member_b', 'member_c'), ('member_b', 'member_d'), ('member_c', 'member_d')]
概要
これらの関数はすべてタプルのイテレータを返します。要素数が多くなると生成される組み合わせ数は爆発的に増加するため注意が必要です。例えばn個からr個を選ぶ順列の数はP(n,r) = n!/(n-r)!、組み合わせの数はC(n,r) = n!/(r!(n-r)!)となります。
『product()』のrepeatオプションを使うと、同じイテラブルを複数回使った直積を効率的に生成できます。ネストしたforループで同じ結果を得ることもできますが、productを使う方が簡潔に書けます。ブルートフォース探索やテストケースの自動生成などで活用されます。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。