組み合わせの総数を計算する方法を説明します。
また、実行時間の比較を示します。比較の結果は下記です。
- 正確な値、近似した値共にscipy.special.combの実行時間が短いです。
組み合わせの総数
math.comb
math.combはpython3.8で追加されました。
math — 数学関数 — Python 3.9.4 ドキュメント
import math print(math.comb(3, 2)) """出力 3 """
math.factorial
下記、公式の計算をmath.factorialを使用して行う方法(*1)です。
$$\frac{n!}{r!(n-r)!}$$
公式は、下記を参照してください。
【3分で分かる!】順列と組み合わせの違いと公式をわかりやすく(練習問題つき) | 合格サプリ
(*1)math.factorialは、階乗をを整数で返します。math — 数学関数 — Python 3.9.4 ドキュメント
def nCr(n, r):
f = math.factorial
return f(n) // f(r) // f(n-r)
print(nCr(3, 2))
"""出力
3
"""
scipy.special.comb
scipy.special.comb — SciPy v1.7.1 Manualを使用した方法です。
from scipy.special import comb print(comb(3, 2, exact=True)) """出力 3 """
引数のexactがFalseの場合は計算結果が浮動小数点精度で近似された値となり、Trueの場合は計算結果が整数となります(近似されていない)。
正確な値を算出したい場合は、exactをTrueにする必要があります。
from scipy.special import comb
# for r in range(40,50):
# print(comb(100, r, exact=False))
"""出力
1.3746234145802806e+28
2.011644021336996e+28
2.825880887116256e+28
3.811653289598673e+28
4.93782357970737e+28
6.144847121413616e+28
7.347099819081498e+28
8.441348728306404e+28
9.320655887504988e+28
9.891308288780802e+28
"""
for r in range(40,50):
print(comb(100, r, exact=True))
"""出力
13746234145802811501267369720
20116440213369968050635175200
28258808871162574166368460400
38116532895986727945334202400
49378235797073715747364762200
61448471214136179596720592960
73470998190814997343905056800
84413487283064039501507937600
93206558875049876949581681100
98913082887808032681188722800
"""
exact=Falseの出力に「1.3746234145802806e+28」(*1)がありますが、exact=Trueの対応する出力は「13746234145802811501267369720」となっております。太文字部を見ると、exact=Falseの場合は近似値になっていることがわかります。
(*1):e+28は10の28乗
実行時間の比較
math.comb 、 math.factorial 、 scipy.special.combの実行時間を比較しました。条件は下記です。
- nは10000固定
- rは1から10000まで300刻みで増加。 ※1, 301, 601, …
正確な値、近似した値共にscipy.special.combの実行時間が短いです。
import matplotlib.pyplot as plt
import math
import timeit
from scipy.special import comb
def nCr(n, r):
f = math.factorial
return f(n) // f(r) // f(n-r)
n = 10**4
num = 10
time_nCr = []
time_mathComb = []
time_scipiSpecialComb = []
#ステップ数が300である理由は右記:計算時間の短縮、グラフの点の間隔を調整
r_list = [x for x in range(1, n, 300)]
for r in r_list:
#nCr
result = timeit.timeit("nCr(n, r)", number = num, globals=globals())
#resultはnCrをnum回繰り返した時の値になっているので、nで割ります。
time_nCr.append(result/num)
#math.comb
result = timeit.timeit("math.comb(n, r)", number = num, globals=globals())
time_mathComb.append(result/num)
#scipy.special.comb
result = timeit.timeit("comb(n, r, exact=True)", number = num, globals=globals())
time_scipiSpecialComb.append(result/num)
plt.plot(r_list, time_nCr, marker='o', color='r', linestyle='-', label='nCr')
plt.plot(r_list, time_mathComb, marker='o', color='g', linestyle='-', label='math.comb')
plt.plot(r_list, time_scipiSpecialComb, marker='o', color='b', linestyle='-', label='scipy.special.comb')
plt.xlabel('r', fontsize=18)
plt.ylabel('time(sec)[log]', fontsize=18)
plt.legend(loc='upper left')
plt.yscale('log')
plt.show()
scipy.special.combのexactがTrueの時です。

scipy.special.combのexactがFalseの時です。

参考
Python 組み合わせ(combination) – Qiita
組み合わせを計算する様々な方法
【Python】組み合わせ(nCr) 計算の高速化 – Qiita
組み合わせを計算する様々な方法
Is there a math nCr function in python? – Stack Overflow
組み合わせを計算する様々な方法
Statistics: combinations in Python – Stack Overflow
組み合わせを計算する様々な方法

コメント