Pythonで組み合わせ・順列を計算する方法|math.comb・math.permとitertoolsの使い分け
Pythonで組み合わせの数(nCr)はmath.comb(n, r)、順列の数(nPr)はmath.perm(n, r)、階乗はmath.factorial(n)で計算できます。組み合わせや順列を実際に全部書き出したいときは、itertools.combinations()・itertools.permutations()を使います。
「件数を数える」のがmath、「中身を列挙する」のがitertools、と覚えると迷いません。この記事では、両者の使い分け、重複を許す場合(重複順列・重複組み合わせ)、確率の計算、大きな数をあまりで求める方法まで、実行結果つきで説明します。コードはPython 3.12で実行しています。
まず結論:目的別の使い分け
| やりたいこと | 数学の記号 | Pythonの書き方 | 例(n=5, r=2) |
|---|---|---|---|
| 組み合わせの数 | nCr | math.comb(n, r) | 10 |
| 順列の数 | nPr | math.perm(n, r) | 20 |
| 階乗 | n! | math.factorial(n)(math.perm(n)も同じ) | 120 |
| 組み合わせを列挙 | - | itertools.combinations(data, r) | 10通りのタプル |
| 順列を列挙 | - | itertools.permutations(data, r) | 20通りのタプル |
| 重複順列(nr)を列挙 | nΠr | itertools.product(data, repeat=r) | 25通り |
| 重複組み合わせを列挙 | nHr | itertools.combinations_with_replacement(data, r) | 15通り |
math.comb()とmath.perm()はPython 3.8で追加された関数です(mathモジュールの公式ドキュメント、2026年10月15日確認)。
math.comb()・math.perm()・math.factorial()で数を計算する
import math
print(math.comb(5, 2))
print(math.perm(5, 2))
print(math.factorial(5))
print(math.perm(5))
print(math.comb(3, 5))
print(math.comb(52, 5))
10
20
120
120
0
2598960
math.comb(5, 2)は「5人から2人を選ぶ」選び方の数で、5C2 = 10です。math.perm(5, 2)は「5人から2人を選んで並べる」並べ方の数で、5P2 = 20です。math.perm(n)のようにrを省略するとn!(階乗)になります。- r が n より大きいときは0が返ります(エラーにはなりません)。
- 52枚のトランプから5枚を選ぶ組み合わせは2,598,960通りです。Pythonの整数には桁数の上限がないので、大きな数でも正確に計算できます。
公式は、nPr = n! ÷ (n−r)!、nCr = n! ÷ (r! × (n−r)!) です。順列は「選んで並べる」、組み合わせは「選ぶだけ(並び順は区別しない)」なので、nCr = nPr ÷ r! という関係があります(20 ÷ 2 = 10)。
Python 3.15で追加されたmath.integerモジュール
2026年10月9日に公開されたPython 3.15.0では、整数向けの関数をまとめたmath.integerモジュールが追加されました。math.integer.comb()・math.integer.perm()・math.integer.factorial()が本体で、従来のmath.comb()などは互換性のための別名として残ります。公式ドキュメントでは、この別名は3.15から「soft deprecated」(削除予定はないが、新しいコードではmath.integer側の利用を勧める扱い)とされています(2026年10月15日確認)。3.14以前でも動かす必要があるコードは、これまでどおりmath.comb()と書くのが無難です。この記事のコードは3.12で実行しているため、math.comb()で書いています。
itertoolsで組み合わせ・順列を列挙する
from itertools import combinations, permutations
members = ["A", "B", "C", "D"]
print(list(combinations(members, 2)))
print(list(permutations(members, 2))[:6])
print(len(list(permutations(members, 2))))
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D')]
12
combinations()は並び順を区別しないので(‘A’, ‘B’)だけが出ます。permutations()は(‘A’, ‘B’)と(‘B’, ‘A’)を別のものとして出します。どちらもイテレータ(必要になったときに1つずつ作る仕組み)を返すので、中身を見るにはlist()で変換するか、forで回します。
件数だけ知りたいときにlen(list(...))とすると、全部を作ってから数えるので遅くなります。数はmath.comb()・math.perm()で求めてください。
すべての組み合わせ(部分集合)を列挙する
「全組み合わせ」を出したいときは、rを0から要素数まで変えてcombinations()を回します。
from itertools import combinations
items = ["ペン", "ノート", "定規"]
for r in range(len(items) + 1):
for c in combinations(items, r):
print(c)
()
('ペン',)
('ノート',)
('定規',)
('ペン', 'ノート')
('ペン', '定規')
('ノート', '定規')
('ペン', 'ノート', '定規')
3個の要素なら2の3乗で8通りです。要素数が20を超えると100万通りを超えるので、全列挙が現実的かどうかを先にmath.comb()で確かめておくと安全です。
重複順列・重複組み合わせ
from itertools import combinations_with_replacement, product
print(list(product("01", repeat=3)))
print(list(combinations_with_replacement("ABC", 2)))
[('0', '0', '0'), ('0', '0', '1'), ('0', '1', '0'), ('0', '1', '1'), ('1', '0', '0'), ('1', '0', '1'), ('1', '1', '0'), ('1', '1', '1')]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
product(…, repeat=3)は同じものを何度選んでもよく、順番も区別する「重複順列」です。3桁の2進数を全部作る例になっています。combinations_with_replacement()は同じものを何度選んでもよいが順番は区別しない「重複組み合わせ」です。
件数は次の式で計算できます。重複順列はnr、重複組み合わせ(nHr)はn+r−1Crです。
import math
from itertools import product
n, r = 5, 3
print(len(list(product(range(n), repeat=r))), pow(n, r))
print(math.comb(n + r - 1, r))
125 125
35
同じ値が入っているときの注意
itertoolsの公式ドキュメントにあるとおり、combinations()は要素を「値」ではなく「位置」で区別します。また、出力の順番は入力の並び順に従います。
from itertools import combinations
print(list(combinations([1, 1, 2], 2)))
print(set(combinations([1, 1, 2], 2)))
print(list(combinations("CBA", 2)))
print(sorted(combinations(sorted("CBA"), 2)))
[(1, 1), (1, 2), (1, 2)]
{(1, 1), (1, 2)}
[('C', 'B'), ('C', 'A'), ('B', 'A')]
[('A', 'B'), ('A', 'C'), ('B', 'C')]
同じ値が複数あると(1, 2)が2回出ます。値として区別したいならsetで重複を除きます。ただし、件数を数えるときにmath.comb()を使うと「位置で区別した数」になるので、同じ値を含む場合は列挙して数える必要があります。辞書順に並んだ結果がほしいときは、先に入力をソートしておきます。
応用:組み合わせで確率を計算する
トランプ52枚から5枚引いたとき、ちょうど2枚がエースになる確率は、(4C2 × 48C3) ÷ 52C5 です。fractions.Fractionを使うと、割り算の誤差なしに分数で求められます。
from fractions import Fraction
from math import comb
p = Fraction(comb(4, 2) * comb(48, 3), comb(52, 5))
print(p, float(p))
2162/54145 0.03992981808107859
約4.0%です。分数の扱いはPythonで分数を計算する方法で詳しく説明しています。
応用:大きなnCrをあまり(mod)で求める
競技プログラミングでは「答えを1,000,000,007で割ったあまりを出力せよ」という問題がよく出ます。Pythonではmath.comb()で正確な値を出してからあまりを取ることもできますが、何度も計算するなら階乗とその逆元を前もって表にしておく方法が速くなります。
from math import comb
MOD = 1_000_000_007
print(comb(100000, 50000) % MOD)
N = 10
fact = [1] * (N + 1)
for i in range(1, N + 1):
fact[i] = fact[i - 1] * i % MOD
inv = [1] * (N + 1)
inv[N] = pow(fact[N], MOD - 2, MOD)
for i in range(N, 0, -1):
inv[i - 1] = inv[i] * i % MOD
def ncr(n, r):
if r < 0 or r > n:
return 0
return fact[n] * inv[r] % MOD * inv[n - r] % MOD
print(ncr(10, 3), comb(10, 3))
149033233
120 120
pow(x, MOD - 2, MOD)はフェルマーの小定理を使った逆元の計算で、MODが素数のときに使えます。最後の行で、自作のncr()がmath.comb()と同じ値になることを確かめています。
エラーになる例
import math
try:
math.comb(-1, 2)
except ValueError as e:
print("ValueError:", e)
try:
math.comb(5.0, 2)
except TypeError as e:
print("TypeError:", e)
ValueError: n must be a non-negative integer
TypeError: 'float' object cannot be interpreted as an integer
負の数はValueError、5.0のような小数(float)はTypeErrorになります。input()やCSVから読んだ値を渡すときは、int()で整数に変換してから使ってください。
よくある質問
Q. Pythonでnが小さくrが大きいとき、math.comb()はエラーになりますか?
エラーにはならず0が返ります。たとえばmath.comb(3, 5)は0です。nやrが負の数のときはValueErrorになります。
Q. 組み合わせの数とitertools.combinations()の件数が合わないのはなぜですか?
itertools.combinations()は要素を値ではなく位置で区別するため、同じ値が入っていると重複した組が出ます。math.comb()も位置で区別した数なので一致しますが、値で数えたい場合はsetで重複を除いてから数えてください。
Q. math.comb()はどのバージョンから使えますか?
Python 3.8から使えます。それより前のバージョンでは、math.factorial(n) // (math.factorial(r) * math.factorial(n – r))で計算できます。Python 3.15ではmath.integer.comb()が追加され、math.comb()はその別名として残っています。
Q. すべての組み合わせを列挙するには?
rを0から要素数まで変えながらitertools.combinations()を回します。要素数nに対して2のn乗通りになるので、nが大きいときは件数を先に確認してください。
Pythonの基礎を手を動かしながら身につけたい人には、テックジムの無料講座があります。テックジムは東京・永田町に本校がある対面のプログラミングスクールで、自分のペースで進め、つまずいたらトレーナーに質問できます。
itertoolsの関連記事
- Python itertools.permutations()とは?順列を生成する方法
- Python itertools.combinations()とは?組み合わせを生成する方法
- Python itertools.product()とは?すべての組み合わせを作る方法
- itertools 関数一覧|Pythonのイテレータ処理を効率化する方法
- Python itertools.chain()とは?複数のリストをつなげる方法
- イテレータ・ジェネレータを日本一わかりやすく解説!メモリ効率が劇的に向上
あわせて読みたい
学習の順序はPython学習のロードマップ、標準で使える機能の一覧はPython標準ライブラリのモジュール一覧にまとめています。





