N choose kの値を評価する最も効率的な方法は何ですか?私が考えるブルートフォースの方法は、n階乗/ k階乗/(n-k)階乗を見つけることです。
より良い戦略は、この 再帰式 に従ってdpを使用することです。 nがkを選択することを評価する他のより良い方法はありますか?
これは私のバージョンで、純粋に整数で動作し(kによる除算は常に整数の商を生成します)、O(k)で高速です:
function choose(n, k)
if k == 0 return 1
return (n * choose(n - 1, k - 1)) / k
シンプルできれいなため、再帰的に作成しましたが、必要に応じて反復ソリューションに変換できます。
おそらくオーバーフローせずに二項係数_(n choose k)
_を計算する最も簡単な方法は、Pascalの三角形を使用することです。分数や乗算は必要ありません。 _(n choose k)
_。 Pascalの三角形のnth
行とkth
エントリが値を提供します。
このページを見てください 。これは、追加のみのO(n^2)
操作であり、動的プログラミングで解決できます。 64ビット整数に収まる任意の数に対して、非常に高速になります。
このような多くの組み合わせを計算する場合、Pascalの三角形を計算するのが最適なオプションです。すでに再帰式を知っているので、ここにいくつかのコードを貼り付けることができると思います。
MAX_N = 100
MAX_K = 100
C = [[1] + [0]*MAX_K for i in range(MAX_N+1)]
for i in range(1, MAX_N+1):
for j in range(1, MAX_K+1):
C[i][j] = C[i-1][j-1] + C[i-1][j];
print C[10][2]
print C[10][8]
print C[10][3]
n!/k!(n-k)!
アプローチの問題は、コストがそれほど大きくないため、!
の問題が非常に急速に増加するため、たとえば[64ビット]の範囲内にあるnCk
の値でも整数、中間計算はそうではありません。 kainawの再帰的加算アプローチが気に入らない場合は、乗法アプローチを試すことができます。
nCk == product(i=1..k) (n-(k-i))/i
ここで、product(i=1..k)
は、i
が値1,2,...,k
をとるときのすべての用語の積を意味します。
最速の方法は、おそらくパスカルの三角形ではなく式を使用することです。後で同じ数で除算することがわかっているときは、乗算を行わないようにしましょう。 k <n/2の場合、k = n-kとします。 C(n、k)= C(n、n-k)であることがわかりました。
n! / (k! x (n-k)!) = (product of numbers between (k+1) and n) / (n-k)!
少なくともこの手法では、以前に乗算した数値で割ることはありません。 (n-k)乗算と(n-k)除算があります。
私は、乗算しなければならない数値と除算しなければならない数値の間でGCDを見つけることにより、すべての除算を回避する方法を考えています。後で編集してみます。
階乗のルックアップテーブルがある場合、C(n、k)の計算は非常に高速になります。