見出し画像

Python 3: Deep Dive (Part 2 - Iterators, Generators): 組み合わせ(セクション8-4/14)

  • `itertools`モジュールは、デカルト積(product)、順列(permutations)、組み合わせ(combinations)などの組み合わせ論の計算を効率的に行うための機能を提供する。

  • これらの関数は遅延イテレータを返すため、大規模なデータセットでもメモリ効率が良く、サイコロの確率計算やトランプのゲームなど、実世界の問題解決に活用できる。

  • `itertools.product`でグリッド生成、`itertools.permutations`で順列の列挙、`itertools.combinations`で組み合わせの生成が可能で、それぞれ重複の有無も制御できる。


複雑な反復処理や組み合わせ論の問題を扱う場合、Pythonの`itertools`モジュールは非常に強力なツールです。この記事では、`itertools`が提供する高度な組み合わせ論のツール、特に順列、組み合わせ、デカルト積に焦点を当てます。これらの関数は、確率問題におけるすべての可能な結果の生成から、計算シミュレーションにおけるグリッドの作成まで、幅広いタスクに不可欠です。


1. `itertools`における組み合わせ論入門

組み合わせ論は、組み合わせ、順列、数え上げを扱う数学の分野です。Pythonのitertoolsモジュールは、反復可能なオブジェクトの順列、組み合わせ、デカルト積を効率的に生成するための関数群を提供します。

これらの関数は遅延イテレータを返すため特に有用です。つまり、要素をその場で生成し、すべての要素を一度に生成する必要がないため、大規模なデータセットでもメモリ効率が良好です。


2. デカルト積

デカルト積の理解

2つの集合AとBのデカルト積は、最初の要素がAから、2番目の要素がBからとなるすべての可能な順序対の集合です。数学的には以下のように表されます:

[ A \times B = { (a, b) | a \in A \text{ かつ } b \in B } ]

この概念は( n )次元に拡張でき、複数の集合のデカルト積を計算することができます。

`itertools.product`の使用

Pythonの`itertools.product`関数は、入力された反復可能オブジェクトのデカルト積を計算します。任意の数の反復可能オブジェクトを受け取り、可能なすべての組み合わせのタプルを生成します。

構文:

itertools.product(*iterables, repeat=1)
  • `*iterables`: 積を計算したい任意の数の反復可能オブジェクト

  • `repeat`: 積を繰り返す回数

実践例

例1:掛け算の表

`itertools.product`を使用して掛け算の表を作成してみましょう。

import itertools

def multiplication_table(n):
    return ((i, j, i * j) for i, j in itertools.product(range(1, n+1), repeat=2))

# 4x4の掛け算の表を生成
for i, j, product in multiplication_table(4):
    print(f"{i} x {j} = {product}")

出力:

1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
...
4 x 3 = 12
4 x 4 = 16

例2:座標グリッドの生成

(-1)から(1)までを(0.5)刻みで座標グリッドを生成する必要があるとします。

def grid(min_val, max_val, step, num_dimensions=2):
    import itertools
    axes = [itertools.takewhile(lambda x: x <= max_val, 
            itertools.count(min_val, step)) for _ in range(num_dimensions)]
    return itertools.product(*axes)

# 2次元グリッドを生成
coordinates = list(grid(-1, 1, 0.5))
print(coordinates)

出力:

[(-1.0, -1.0), (-1.0, -0.5), ..., (1.0, 0.5), (1.0, 1.0)]

3. 順列

順列の理解

順列とは、オブジェクトの集合の全部または一部を、順序を考慮して並べ替えたものです。( n )個の要素から( r )個を選ぶ順列の数は:

[ P(n, r) = \frac{n!}{(n - r)!} ]

`itertools.permutations`の使用

`itertools.permutations`関数は、指定された長さのすべての可能な順列を生成します。

構文:

itertools.permutations(iterable, r=None)
  • `iterable`: データソース

  • `r`: 順列の長さ(デフォルトは反復可能オブジェクトの長さ)

例:

import itertools

data = ['A', 'B', 'C']
perms = list(itertools.permutations(data))
print(perms)

出力:

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

重複要素のある順列

反復可能オブジェクトに重複要素がある場合、`itertools.permutations`は各位置を一意として扱います。

例:

data = ['A', 'A', 'B']
perms = list(itertools.permutations(data))
print(perms)

出力:

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

4. 組み合わせ

組み合わせの理解

組み合わせとは、コレクションから項目を選択する際に、選択順序が問題とならないものを指します。( n )個の要素から( r )個を選ぶ組み合わせの数は:

[ C(n, r) = \frac{n!}{r!(n - r)!} ]

`itertools.combinations`の使用

`itertools.combinations`関数は、指定された長さのすべての可能な組み合わせを重複なしで生成します。

構文:

itertools.combinations(iterable, r)
  • `iterable`: データソース

  • `r`: 組み合わせの長さ

例:

import itertools

data = [1, 2, 3, 4]
combos = list(itertools.combinations(data, 2))
print(combos)

出力:

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

重複を許す組み合わせ

重複要素を許可する組み合わせには、`itertools.combinations_with_replacement`を使用します。

構文:

itertools.combinations_with_replacement(iterable, r)

例:

combos_wr = list(itertools.combinations_with_replacement(data, 2))
print(combos_wr)

出力:

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

5. 実世界での応用

サイコロの確率計算

問題: 2つのサイコロを振って8が出る確率は?

解決方法:

  1. 標本空間の生成:

sample_space = list(itertools.product(range(1, 7), repeat=2))
  1. 合計が8となる結果をフィルタリング:

outcomes = list(filter(lambda x: x[0] + x[1] == 8, sample_space))
  1. 確率の計算:

from fractions import Fraction

odds = Fraction(len(outcomes), len(sample_space))
print(f"8が出る確率:{odds}")

出力:

8が出る確率:5/36

トランプからエースを引く確率

問題: 標準的な52枚のトランプから4枚引いて、すべてがエースである確率は?

解決方法:

  1. デッキの作成:

import itertools
from collections import namedtuple

Card = namedtuple('Card', 'rank suit')
SUITS = 'SHDC'
RANKS = [str(n) for n in range(2, 11)] + list('JQKA')
deck = [Card(rank, suit) for suit, rank in itertools.product(SUITS, RANKS)]
  1. 標本空間の生成:

sample_space = itertools.combinations(deck, 4)
  1. 全体の場合の数と条件を満たす場合の数をカウント:

total = 0
acceptable = 0

for hand in sample_space:
    total += 1
    if all(card.rank == 'A' for card in hand):
        acceptable += 1
  1. 確率の計算:

odds = Fraction(acceptable, total)
print(f"4枚のエースをすべて引く確率:{odds}")

出力:

4枚のエースをすべて引く確率:1/270725

6. 結論

Pythonのitertoolsモジュールは、複雑な組み合わせ論の問題を単純化し、効率的で読みやすい解決策を提供します。シミュレーション用のグリッドの生成、確率の計算、順列や組み合わせの探索など、これらのツールは非常に価値があります。

`itertools.product`、`itertools.permutations`、`itertools.combinations`を理解し活用することで、幅広い計算タスクをエレガントかつ効率的に解決することができます。

参考文献:

ハッピーコーディング!


この記事は参考になりましたか?下のコメント欄で教えていただくか、この記事から恩恵を受けそうな友人や同僚とシェアしてください。


「超本当にドラゴン」へ

いいなと思ったら応援しよう!