Python | データ構造強化:itertools.permutations

Python Python
スポンサーリンク

概要(permutations は「順番を考慮した並べ方」を遅延生成で作る)

itertools.permutations は、イテラブルの要素を「順番を考慮して並べ替えたすべての並び(順列)」を順に返すイテレータです。長さ r を指定でき、結果はタプルで返されます。多重ループを書かずに全並びを列挙でき、結果は遅延生成されるため巨大な組み合わせでも安全に扱えます。

from itertools import permutations

items = ["A", "B", "C"]
for p in permutations(items):        # r を省略 → 全要素の順列(r=3)
    print(p)                         # ('A','B','C'), ('A','C','B'), ...
Python

基本の使い方(ここが重要)

長さ r の指定(r を省略すると全要素)

r を指定すると、元の要素から r 個を「順番あり」で並べた順列を返します。r を省略すると、元の長さすべて(len(iterable))の順列です。

from itertools import permutations

for p in permutations([1, 2, 3], 2):
    print(p)  # (1,2), (1,3), (2,1), (2,3), (3,1), (3,2)
Python

返り値はタプル。必要なら文字列や数字へ変換

並びはタプルで返されます。表示や利用に応じて join や計算へ変換します。

from itertools import permutations

for p in permutations("ABC", 3):
    print("".join(p))  # 'ABC', 'ACB', 'BAC', ...
Python

遅延生成(メモリに全展開しない)

permutations は1個ずつ作るため、巨大な並びでも「必要な分だけ」取り出せます。list(…) で全展開するとメモリが爆発しやすいので注意し、基本は for で流します。

from itertools import permutations, islice

it = permutations(range(1000), 3)
for p in islice(it, 10):  # 先頭10件だけ見る
    print(p)
Python

重要ポイントの深掘り(サイズ計算・重複要素・比較対象)

通り数(サイズ)は階乗や置換で爆発する

  • 全要素の順列数は n!(n の階乗)。
  • 長さ r の順列数は nPr = n × (n-1) × … × (n-r+1)。

要素数が少し増えるだけで通り数が急増するため、実務では「打ち切り」「条件で絞り込み」が必須です。

import math
n = 6
print(math.factorial(n))     # 720(6!)
def nPr(n, r): 
    out = 1
    for k in range(r): out *= (n - k)
    return out
print(nPr(10, 3))            # 720(10P3)
Python

入力に重複があると、重複した順列が出る

permutations は入力の各要素を位置で区別します。入力に同じ値が複数あると、重複結果が出ます。重複排除したい場合は一度ソートしてから、集合や辞書で弾く、あるいは独自フィルタを噛ませます。

from itertools import permutations

items = ["A", "A", "B"]
uniq = set("".join(p) for p in permutations(items, 2))
print(sorted(uniq))  # 重複排除した並び ['AA', 'AB', 'BA']
Python

combinations と product との違い

  • combinations は「順番を無視した取り合わせ」。(A,B) と (B,A) は同じとして1通り。
  • permutations は「順番あり」。(A,B) と (B,A) は別通り。
  • product は「直積」。別の集合を組み合わせる全通り(重複選択も可能)。

用途に応じて使い分けると、無駄な列挙を避けられます。


実務での使いどころ(並び検討・探索・テストケース)

順番が意味を持つ並べ替えの網羅(並べ順最適化の下準備)

タスク順序、処理パイプライン、UI 配置など、「順序によって結果が変わる」ものの当たり検討に向きます。評価関数で絞りながら逐次処理しましょう。

from itertools import permutations

tasks = ["parse", "filter", "export"]
best = None
best_score = float("-inf")

def score(order): 
    # ダミー評価。実務では計測や制約を入れる
    return 100 - 10 * order.index("export")

for order in permutations(tasks):
    s = score(order)
    if s > best_score:
        best, best_score = order, s

print(best, best_score)
Python

パス・ファイル名の順列生成(少数なら一括、巨大なら条件絞り)

短い接頭辞・拡張子・日付などを並べ替えながら探索するケースで便利です。件数が多くなる場合は、早めの条件判定で落としていきます。

from itertools import permutations

for name in permutations(["report", "2025-12-15", "final"], 2):
    print("_".join(name))  # 'report_2025-12-15', 'report_final', ...
Python

テスト ID・シナリオ順列(部分取り出しで現実的に)

大量になるため、islice で一部サンプルだけ採取する、ランダムサンプリングする、制約で削減するのが定石です。

from itertools import permutations, islice
import random

cases = permutations("ABCDE", 3)
sample = list(islice(cases, 20))  # 先頭20件だけ
random.shuffle(sample)
for c in sample[:5]:
    print(c)
Python

例題で身につける(定番から一歩先まで)

例題1:3桁の並びで条件を満たすものだけ列挙

from itertools import permutations

def valid_codes():
    for a, b, c in permutations(range(5), 3):
        # 隣り合う差が1以下、合計が奇数などの条件例
        if abs(a - b) <= 1 and abs(b - c) <= 1 and ((a + b + c) % 2 == 1):
            yield (a, b, c)

for code in list(valid_codes())[:10]:
    print(code)
Python

例題2:旅行ルートの総当たり(簡易版)

from itertools import permutations

cities = ["Tokyo", "Osaka", "Nagoya", "Fukuoka"]

def route_cost(route):
    # ダミーコスト。距離テーブルがある想定で計算する
    return len(route) * 10

best, best_cost = None, float("inf")
for route in permutations(cities):
    cost = route_cost(route)
    if cost < best_cost:
        best, best_cost = route, cost

print("best:", best, best_cost)
Python

例題3:重複入力の重複排除順列(ユニークだけ取りたい)

from itertools import permutations

items = ["A", "A", "B", "C"]
def unique_perms(iterable, r):
    seen = set()
    for p in permutations(sorted(iterable), r):
        if p not in seen:
            seen.add(p)
            yield p

for p in unique_perms(items, 3):
    print(p)
Python

例題4:パスワード候補(英字+数字の並び)を一部だけ生成

from itertools import permutations
import string

letters = string.ascii_lowercase[:4]  # 'a','b','c','d'
digits  = "0123"
for cand in permutations(letters + digits, 3):
    s = "".join(cand)
    if s[0].isalpha() and any(ch.isdigit() for ch in s):
        print(s)
        break  # 条件を満たす最初の1件だけ
Python

まとめ

itertools.permutations は「順番を考慮した並べ替え(順列)」を遅延生成で安全に列挙する基本ツールです。r 指定で部分順列、返り値はタプル、巨大化しやすいのでサイズ見積もりと早期絞り込みが要。入力に重複があると重複結果が出る点に注意し、必要ならユニーク化を組み合わせる。combinations(順番なし)や product(直積)と正しく使い分ければ、並べ順の網羅、探索、テスト列挙が短く、読みやすく、堅牢に書けます。

タイトルとURLをコピーしました