Pythonで階乗、順列・組み合わせを計算、生成
Pythonではmath
モジュールを使って階乗や順列・組み合わせの総数を算出できる。SciPyでも順列・組み合わせの総数を算出する関数が提供されている。
また、itertools
モジュールを使ってリストなどから順列・組み合わせを生成して列挙することも可能。
なお、単独のリストの要素の組み合わせではなく、複数のリストの要素の組み合わせを生成したい場合はitertools.product()
を使う。
階乗: math.factorial()
階乗を算出するにはmath.factorial()
を使う。
import math
print(math.factorial(5))
# 120
print(math.factorial(0))
# 1
非整数値、負の値を指定するとエラーになる。
# print(math.factorial(1.5))
# TypeError: 'float' object cannot be interpreted as an integer
# print(math.factorial(-1))
# ValueError: factorial() not defined for negative values
順列の総数を算出
順列は、異なるn
個のものからk
個選んで一列に並べる場合の数。
順列の総数p
は階乗を使って以下の式で求められる。!
は階乗を表す。
p = n! / (n - k)!
math.perm()
Python 3.8で順列の総数を返す関数math.perm()
が追加された。
n
個からk
個選ぶとき、math.perm(n, k)
と指定する。
import math
print(math.perm(4, 2))
# 12
print(math.perm(4, 4))
# 24
非整数値、負の値を指定するとエラーになる。
# print(math.perm(4.5, 2.5))
# TypeError: 'float' object cannot be interpreted as an integer
# print(math.perm(-4, -2))
# ValueError: n must be a non-negative integer
Python 3.7以前のバージョンでは、math.factorial()
を使って以下のように算出できる。整数int
を返すように、整数除算を行う//
演算子を用いている。
def permutations_count(n, k):
return math.factorial(n) // math.factorial(n - k)
print(permutations_count(4, 2))
# 12
print(permutations_count(4, 4))
# 24
scipy.special.perm()
SciPyにも順列の総数を返す関数scipy.special.perm()
が用意されている。
n
個からk
個選ぶとき、scipy.special.perm(n, k)
と指定する。
from scipy.special import perm
print(perm(4, 2))
# 12.0
print(perm(4, 2, exact=True))
# 12
print(perm(4, 4, exact=True))
# 24
デフォルトは第三引数exact=False
で、浮動小数点数float
が返される。整数int
で取得したい場合はexact=True
とする。
なお、import scipy
だとscipy.special
モジュールは読み込まれないので注意。上の例のようにfrom scipy.special import perm
としてperm()
を実行するか、import scipy.special
としてscipy.special.perm()
を実行する。
リストから順列を生成・列挙: itertools.permutations()
リストなどから順列を生成して列挙するには、itertools.permutations()
を使う。
第一引数にイテラブル(リストや集合set
など)、第二引数に選択する個数を渡すと、その順列のイテレータを返す。
import itertools
l = ['a', 'b', 'c', 'd']
p = itertools.permutations(l, 2)
print(type(p))
# <class 'itertools.permutations'>
すべて列挙するにはforループを使う。
for v in itertools.permutations(l, 2):
print(v)
# ('a', 'b')
# ('a', 'c')
# ('a', 'd')
# ('b', 'a')
# ('b', 'c')
# ('b', 'd')
# ('c', 'a')
# ('c', 'b')
# ('c', 'd')
# ('d', 'a')
# ('d', 'b')
# ('d', 'c')
list()
でタプルを要素とするリストに変換できる。
p_list = list(itertools.permutations(l, 2))
print(p_list)
# [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'a'), ('b', 'c'), ('b', 'd'), ('c', 'a'), ('c', 'b'), ('c', 'd'), ('d', 'a'), ('d', 'b'), ('d', 'c')]
print(len(p_list))
# 12
第二引数を省略すると、すべての要素を選択する場合の順列が返される。
for v in itertools.permutations(l):
print(v)
# ('a', 'b', 'c', 'd')
# ('a', 'b', 'd', 'c')
# ('a', 'c', 'b', 'd')
# ('a', 'c', 'd', 'b')
# ('a', 'd', 'b', 'c')
# ('a', 'd', 'c', 'b')
# ('b', 'a', 'c', 'd')
# ('b', 'a', 'd', 'c')
# ('b', 'c', 'a', 'd')
# ('b', 'c', 'd', 'a')
# ('b', 'd', 'a', 'c')
# ('b', 'd', 'c', 'a')
# ('c', 'a', 'b', 'd')
# ('c', 'a', 'd', 'b')
# ('c', 'b', 'a', 'd')
# ('c', 'b', 'd', 'a')
# ('c', 'd', 'a', 'b')
# ('c', 'd', 'b', 'a')
# ('d', 'a', 'b', 'c')
# ('d', 'a', 'c', 'b')
# ('d', 'b', 'a', 'c')
# ('d', 'b', 'c', 'a')
# ('d', 'c', 'a', 'b')
# ('d', 'c', 'b', 'a')
print(len(list(itertools.permutations(l))))
# 24
itertools.permutations()
では、要素が値ではなく位置に基づいて扱われる。値が重複していても特に考慮されない。
l = ['a', 'a']
for v in itertools.permutations(l, 2):
print(v)
# ('a', 'a')
# ('a', 'a')
以下で説明する、itertools.combinations()
, combinations_with_replacement()
でも同様。
組み合わせの総数を算出
組み合わせは、異なるn
個のものからk
個選ぶ場合の数。順列のように順番を考慮しない。
組み合わせの総数c
は以下の式で求められる。二項係数とも呼ばれる。
c = n! / (k! * (n - k)!)
math.comb()
Python 3.8で組み合わせの総数を返す関数math.comb()
が追加された。
n
個からk
個選ぶとき、math.comb(n, k)
と指定する。
import math
print(math.comb(4, 2))
# 6
非整数値、負の値を指定するとエラーになる。
# print(math.comb(4.5, 2.5))
# TypeError: 'float' object cannot be interpreted as an integer
# print(math.comb(-4, -2))
# ValueError: n must be a non-negative integer
Python 3.7以前のバージョンでは、math.factorial()
を使って以下のように算出できる。整数int
を返すように、整数除算を行う//
演算子を用いている。
def combinations_count(n, k):
return math.factorial(n) // (math.factorial(n - k) * math.factorial(k))
print(combinations_count(4, 2))
# 6
scipy.special.comb()
SciPyにも組み合わせの総数を返す関数scipy.special.comb()
が用意されている。
from scipy.special import comb
print(comb(4, 2))
# 6.0
print(comb(4, 2, exact=True))
# 6
print(comb(4, 0, exact=True))
# 1
scipy.special.perm()
と同様に、デフォルトは第三引数exact=False
で、浮動小数点数float
が返される。整数int
で取得したい場合はexact=True
とする。
第四引数repetition
で重複組み合わせの総数も取得可能。後述。
繰り返しになるが、import scipy
だとscipy.special
モジュールは読み込まれないので注意。上の例のようにfrom scipy.special import comb
としてcomb()
を実行するか、import scipy.special
としてscipy.special.comb()
を実行する。
リストから組み合わせを生成・列挙: itertools.combinations()
リストなどからすべての組み合わせを生成して列挙するには、itertools.combinations()
を使う。
第一引数にイテラブル(リストや集合set
など)、第二引数に選択する個数を渡すと、その組み合わせのイテレータを返す。list()
でリストに変換可能。
import itertools
l = ['a', 'b', 'c', 'd']
c = itertools.combinations(l, 2)
print(type(c))
# <class 'itertools.combinations'>
for v in itertools.combinations(l, 2):
print(v)
# ('a', 'b')
# ('a', 'c')
# ('a', 'd')
# ('b', 'c')
# ('b', 'd')
# ('c', 'd')
c_list = list(itertools.combinations(l, 2))
print(c_list)
# [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
print(len(c_list))
# 6
重複組み合わせの総数を算出
重複組み合わせは、異なるn
個のものから重複を許してk
個選ぶ場合の数。
重複組み合わせの総数は、異なるn + k - 1
個のものからk
個選ぶ組み合わせの数に等しい。したがって、math.comb()
、または、上で定義した組み合わせの総数を算出する関数を使えばよい。
import math
def combinations_with_replacement_count(n, k):
return math.comb(n + k - 1, k)
print(combinations_with_replacement_count(4, 2))
# 10
def combinations_with_replacement_count(n, k):
return combinations_count(n + k - 1, k)
print(combinations_with_replacement_count(4, 2))
# 10
上述のscipy.special.comb()
では、第四引数repetition=True
とすると重複組合せの総数が取得可能。
from scipy.special import comb
print(comb(4, 2, exact=True, repetition=True))
# 10
リストから重複組み合わせを生成・列挙: itertools.combinations_with_replacement()
リストなどからすべての重複組み合わせを生成して列挙するには、itertools.combinations_with_replacement()
を使う。
第一引数にイテラブル(リストや集合set
など)、第二引数に選択する個数を渡すと、その重複組み合わせのイテレータを返す。list()
でリストに変換可能。
import itertools
l = ['a', 'b', 'c', 'd']
h = itertools.combinations_with_replacement(l, 2)
print(type(h))
# <class 'itertools.combinations_with_replacement'>
for v in itertools.combinations_with_replacement(l, 2):
print(v)
# ('a', 'a')
# ('a', 'b')
# ('a', 'c')
# ('a', 'd')
# ('b', 'b')
# ('b', 'c')
# ('b', 'd')
# ('c', 'c')
# ('c', 'd')
# ('d', 'd')
h_list = list(itertools.combinations_with_replacement(l, 2))
print(h_list)
# [('a', 'a'), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'b'), ('b', 'c'), ('b', 'd'), ('c', 'c'), ('c', 'd'), ('d', 'd')]
print(len(h_list))
# 10