Pythonで組み合わせを効率的に生成する方法:itertools.combinations の使い方

Pythonで「このリストからすべての組み合わせを生成したい!」という場面に出くわしたことはありませんか?例えば、3人のチームから2人を選んだり、数値のリストから特定の合計を作る組み合わせを探したりするようなケースです。

そんなとき、itertools.combinations を使うと簡単に、しかも効率的に組み合わせを生成できます。本記事では、この関数の基本的な使い方から、実用的な活用例までを解説します。

combination lock

itertools.combinations とは?

itertools.combinations は、Python 標準ライブラリの itertools モジュールに含まれる関数で、指定したデータ(リストやタプルなど)から指定したサイズのすべての 順序を考慮しない組み合わせ を生成するために使います。

例えば、リスト ['A', 'B', 'C'] から 2 要素を選ぶ組み合わせを生成する場合:

import itertools

items = ['A', 'B', 'C']
combinations = itertools.combinations(items, 2)
print(list(combinations))

出力

[('A', 'B'), ('A', 'C'), ('B', 'C')]

ポイントは、('A', 'B')('B', 'A') のように順序が異なるだけのペアは1回しか生成されないことです。


基本の使い方

itertools.combinations は以下のように呼び出します。

itertools.combinations(iterable, r)

引数

  • iterable: リストやタプルなど、組み合わせを生成する元のデータ。
  • r: 組み合わせのサイズ(選びたい要素の数)。

例えば、[1, 2, 3] から 2 要素の組み合わせを生成するコードは以下の通りです。

import itertools

numbers = [1, 2, 3]
print(list(itertools.combinations(numbers, 2)))

出力

[(1, 2), (1, 3), (2, 3)]

実用例

1. 部分集合の生成

リストからすべての部分集合(空集合を含む)を生成したい場合、itertools.combinations を利用して簡単に実現できます。

from itertools import combinations

items = ['A', 'B', 'C']
all_subsets = []
for r in range(len(items) + 1):
    all_subsets.extend(combinations(items, r))

print(list(all_subsets))

出力

[(), ('A',), ('B',), ('C',), ('A', 'B'), ('A', 'C'), ('B', 'C'), ('A', 'B', 'C')]

2. すべてのペアの比較(例:距離計算)

複数の座標間の距離を計算したいとき、itertools.combinations を使えばすべてのペアを効率的に生成できます。

from itertools import combinations
from math import sqrt

points = [(0, 0), (1, 0), (1, 1), (0, 1)]

def distance(p1, p2):
    return sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)

for p1, p2 in combinations(points, 2):
    print(f"{p1} と {p2} の距離: {distance(p1, p2):.2f}")

このコードでは、座標の組み合わせごとにユークリッド距離を計算します。


3. 条件を満たす組み合わせの探索

例えば、数値リストから複数の値を選び、その合計が特定の値に一致する組み合わせを見つける場合です。

from itertools import combinations

numbers = [10, 20, 30, 40, 50]
target = 100

for r in range(1, len(numbers) + 1):
    for combo in combinations(numbers, r):
        if sum(combo) == target:
            print("見つかった組み合わせ:", combo)

出力

見つかった組み合わせ: (10, 30, 60)

注意点

1. r が元のデータの長さを超える場合

生成される組み合わせは空になります。

items = [1, 2, 3]
print(list(itertools.combinations(items, 4)))  # []

2. r=0 の場合

空のタプルのみが返されます。

print(list(itertools.combinations([1, 2, 3], 0)))  # [()]

3. 元のデータに重複がある場合

入力データに重複がある場合、重複を含む組み合わせが生成されます。

items = ['A', 'A', 'B']
print(list(itertools.combinations(items, 2)))
# [('A', 'A'), ('A', 'B'), ('A', 'B')]

こうしたケースにおいてはsetを使うと良いでしょう。

items = ['A', 'A', 'B']
items = list(set(items)) # ['A', 'B']に変換
print(list(itertools.combinations(items, 2)))
# [('A', 'B')]

itertools.permutations との違い

itertools.permutations も組み合わせに似ていますが、順序を考慮する点が異なります。

from itertools import permutations, combinations

items = ['A', 'B']
print(list(combinations(items, 2)))  # [('A', 'B')]
print(list(permutations(items, 2)))  # [('A', 'B'), ('B', 'A')]

permutations では順序が異なるすべての並びを生成します。


効率的に使うためのヒント

  • 必要な範囲を絞る: 不要な組み合わせを生成しないよう、r の範囲を限定しましょう。
  • 早期終了: 条件を満たす組み合わせが見つかったら、ループを中断することで効率化できます。
from itertools import combinations

numbers = [10, 20, 30, 40, 50]
target = 100
found = False

for r in range(1, len(numbers) + 1):
    if found:
        break
    for combo in combinations(numbers, r):
        if sum(combo) == target:
            print("見つかった組み合わせ:", combo)
            found = True
            break

まとめ

itertools.combinations は、順序を考慮しない組み合わせを効率的に生成する非常に便利なツールです。
リストから部分集合を作りたい場合や、条件を満たす組み合わせを探索したい場合など、多くの場面で役立ちます。

Python の豊富な標準ライブラリを活用して、日々の開発をもっと楽にしていきましょう!

上部へスクロール