おそらく、計算したい値は「n-choose-k」であり、一度にk個のn個のものの組み合わせの数です。そのための式は ですn!/(k! * (n-k)!)
。欠損*
値を計算に追加するとn!/k! * (n-k)!
、 に等しい が生成されます(n!/k!)*(n-k)!
。(注、k!
均等に分割n!
します。)たとえば、n=10 で k=5 の場合、C(10,5) は 3628800/(120*120) = 252 になるはずですが、計算すると 3628800/120*120 = 3628800 になります。これは 14400 倍間違っています。
もちろん、括弧を修正できます。
>>> C = lambda n,k: math.factorial(n)/(math.factorial(k)*math.factorial(n-k))
>>> C(10,5)
252
しかし、math.factorial(j) が計算に j-1 回の乗算を必要とする場合、C(n,k) は n-1+k-1+nk-1+1 = 2*n-2 回の乗算と 1 つの除算を必要とすることに注意してください。これは、必要な乗算操作の約 4 倍です。以下に示すコードは、j 回の乗算と j 回の除算を使用します。ここで、j は k と nk の小さい方であるため、j は最大でも n/2 です。一部のマシンでは、除算は乗算よりもはるかに遅くなりますが、ほとんどのマシンでは、j の乗算と j の除算は、2*n-2 の乗算と 1 つの除算よりもはるかに高速に実行されます。
さらに重要なことは、C(n,k) が n! よりもはるかに小さいことです。式による計算では、n!/(k!*(n-k)!)
n が 20 を超える場合は常に 64 ビット以上の精度が必要です。たとえば、C(21,1) は値 21L を返します。対照的に、以下のコードは、D(62,31)=465428353255261088L を計算するために 64 ビット以上を必要とする前に、D(61,30)=232714176627630544 まで計算します。(名前の衝突を避けるために、以下の関数に「C」ではなく「D」という名前を付けました。)
大きな高速マシンでの小さな計算では、余分な乗算と余分な精度の要件は重要ではありません。ただし、小さなマシンでの大規模な計算では、それらが重要になります。
つまり、D() の乗算と除算の順序により、最小限に見える最大の中間値が維持されます。最大値は、for ループの最後のパスに表示されます。また、for ループでは、i は常に c*j の正確な除数であり、切り捨ては行われないことに注意してください。これは、「n-choose-k」を計算するためのかなり標準的なアルゴリズムです。
def D(n, k):
c, j, k = 1, n, min(k,n-k)
for i in range(1,k+1):
c, j = c*j/i, j-1
return c
通訳からの結果:
>>> D(10,5)
252
>>> D(61,30)
232714176627630544
>>> D(62,31)
465428353255261088L