コードを見ると、すぐにメモリ不足になってしまうのも不思議ではありません。メソッドは factorialメソッドdivideFactorials
を呼び出し、引数として「分子 - 分母」の差を使用します。コードによると、その差は非常に大きくなる可能性が非常に高く、階乗法で非常に長いループに陥ります。
それが本当にnCkを見つけることだけである場合(コード内のコメントのためだと思います)、次を使用してください:
public static long GetnCk(long n, long k)
{
long bufferNum = 1;
long bufferDenom = 1;
for(long i = n; i > Math.Abs(n-k); i--)
{
bufferNum *= i;
}
for(long i = k; i => 1; i--)
{
bufferDenom *= i;
}
return (long)(bufferNom/bufferDenom);
}
もちろん、この方法を使用すると、 long は実際には非常に長い数値をサポートしていないため、範囲を非常に速く使い果たします。そのため、n と k は 20 よりも小さくする必要があります。
実際に非常に大きな数を扱うと仮定すると、累乗がますます重要になるため、long の代わりに double を使用できます。
編集:
大きな数を使用する場合は、スターリングの式も使用できます:
n が大きくなると ln(n!) -> n*ln(n) - n.
これをコードに入れる:
public static double GetnCk(long n, long k)
{
double buffern = n*Math.Log(n) - n;
double bufferk = k*Math.Log(k) - k;
double bufferkn = Math.Abs(n-k)*Math.Log(Math.Abs(n-k)) - Math.Abs(n-k);
return Math.Exp(buffern)/(Math.Exp(bufferk)*Math.Exp(bufferkn));
}
あなたが言語に依存しないと言ったように、私はこの答えを提案するだけです。C#コードはそれを実証するために使用されています。これを機能させるには n と k に大きな数を使用する必要があるため、大きな組み合わせの二項係数を見つけるための一般的な方法としてこれを提案します。
n と k の両方が 200 ~ 300 よりも小さい場合は、正確であるため、Victor Mukherjee が提案した答えを使用する必要があります。
Edit2:
最初のコードを編集しました。