Алгоритм вычисления биномиального коэффициента
Мне нужен способ вычисления комбинаций без запуска из памяти. Вот что у меня пока есть.
public static long combination(long n, long k) // nCk
{
return (divideFactorials(factorial(n), ((factorial(k) * factorial((n - k))))));
}
public static long factorial(long n)
{
long result;
if (n <= 1) return 1;
result = factorial(n - 1) * n;
return result;
}
public static long divideFactorials(long numerator, long denominator)
{
return factorial(Math.Abs((numerator - denominator)));
}
я пометил его как C#, но решение в идеале должно быть независимым от языка.
5 ответов
public static long combination(long n, long k)
{
double sum=0;
for(long i=0;i<k;i++)
{
sum+=Math.log10(n-i);
sum-=Math.log10(i+1);
}
return (long)Math.pow(10, sum);
}
одним из лучших методов вычисления биномиального коэффициента, который я видел, является Марк Доминус. Гораздо менее вероятно переполнение с большими значениями для N и K, чем некоторые другие методы.
public static long GetBinCoeff(long N, long K)
{
// This function gets the total number of unique combinations based upon N and K.
// N is the total number of items.
// K is the size of the group.
// Total number of unique combinations = N! / ( K! (N - K)! ).
// This function is less efficient, but is more likely to not overflow when N and K are large.
// Taken from: http://blog.plover.com/math/choose.html
//
long r = 1;
long d;
if (K > N) return 0;
for (d = 1; d <= K; d++)
{
r *= N--;
r /= d;
}
return r;
}
вот решение, которое очень похоже на Боба байрана, но проверяет еще два предварительных условия для ускорения кода.
/// <summary>
/// Calculates the binomial coefficient (nCk) (N items, choose k)
/// </summary>
/// <param name="n">the number items</param>
/// <param name="k">the number to choose</param>
/// <returns>the binomial coefficient</returns>
public static long BinomCoefficient(long n, long k)
{
if (k > n) { return 0; }
if (n == k) { return 1; } // only one way to chose when n == k
if (k > n - k) { k = n - k; } // Everything is symmetric around n-k, so it is quicker to iterate over a smaller k than a larger one.
long c = 1;
for (long i = 1; i <= k; i++)
{
c *= n--;
c /= i;
}
return c;
}
глядя на ваш код, неудивительно, что у вас закончится память довольно быстро. Ваш метод divideFactorials
вызывает метод factorial и использует в качестве аргумента разность "числитель-знаменатель". Эта разница, скорее всего, будет очень большой в соответствии с вашим кодом, и вы застрянете в очень длинном цикле в своем факториальном методе.
Если это действительно только о поиске 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);
}
Of конечно, используя этот метод, вы очень быстро выйдете из диапазона, потому что long фактически не поддерживает очень длинные числа, поэтому n и k должны быть меньше 20.
предположим, что вы действительно работаете с очень большими числами, вы можете использовать двойники вместо длинных, поскольку силы становятся все более значимыми.
Edit: Если вы используете большие числа, вы также можете использовать формулу Стирлинга:
как 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, вы должны использовать ответ Виктор Mukherjee предложил, как это точный.
Edit2: Отредактировал мой первый код.