我需要一个能够计算纸牌游戏的mathematical combination(n,k)的函数。
我目前的尝试是使用基于通常的Factorial方法的函数:
static long Factorial(long n)
{
return n < 2 ? 1 : n * Factorial(n - 1);
}
static long Combinatory(long n , long k )
{
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}
它工作得非常好,但问题是当我使用一些数字范围时(n值最大值为52且k值max最大值为4),它会让我返回错误值。例如:
long comb = Combinatory(52, 2) ; // return 1 which should be actually 1326
我知道这是因为当我制作Factorial(52)时,我已经过了很长时间,但我需要的范围结果并不像它看起来那么大。
有没有办法解决这个问题?
答案 0 :(得分:10)
使用组合函数的递归属性,而不是使用默认的组合公式n! / (k! x (n - k)!)
。
(n, k) = (n - 1, k) + (n - 1, k - 1)
了解:(n, 0) = 1
和(n, n) = 1
。
<强> - &GT;它会让你避免使用阶乘和溢出你的长期。
以下是您可以执行的实施示例:
static long Combinatory(long n, long k)
{
if (k == 0 || n == k )
return 1;
return Combinatory(n - 1, k) + Combinatory(n - 1, k - 1);
}
编辑:使用更快的迭代算法
static long Combinatory(long n, long k)
{
if (n - k < k)
k = n - k;
long res = 1;
for (int i = 1; i <= k; ++i)
{
res = (res * (n - i + 1)) / i;
}
return res;
}
答案 1 :(得分:2)
在C#中你可以使用BigInteger(我认为有一个Java等价物。)
e.g:
static long Combinatory(long n, long k)
{
return (long)(Factorial(new BigInteger(n)) / (Factorial(new BigInteger(k)) * Factorial(new BigInteger(n - k))));
}
static BigInteger Factorial(BigInteger n)
{
return n < 2 ? 1 : n * Factorial(n - 1);
}
您需要添加对System.Numerics
的引用才能使用BigInteger。
答案 2 :(得分:2)
如果这不是家庭作业,那么在Apache的公共数学包中有一个有效的实现
如果是家庭作业,请在实施中开始避免使用因子。
使用(n,k)=(n,n-k)的属性使用k的最高值重写您的选择。
然后注意你可以减少n!/ k!(n-k)!到n * n-1 * n-2 .... * k /(nk)*(nk-1)... * 1表示您将[k,n]中的每个数相乘,然后除以数字[1,nk]包括在内。
// From memory, please verify correctness independently before trusting its use.
//
public long choose(n, k) {
long kPrime = Math.max(k, n-k);
long returnValue = 1;
for(i = kPrime; i <= n; i++) {
returnValue *= i;
}
for(i = 2; i <= n - kPrime; i++) {
returnValue /= i;
}
return returnValue;
}
请仔细检查数学,但这是一个基本的想法,你可以采取一个合理有效的实施,适用于数字到扑克牌。
答案 3 :(得分:0)
递归公式也称为Pascal's triangle,IMO是计算组合的最简单方法。如果您只需要C(52,k)(对于0 <= k <= 52),我认为最好在程序启动时用它们填充表格。以下C代码使用此方法填充表:
static int64_t* pascals_triangle( int N)
{
int n,k;
int64_t* C = calloc( N+1, sizeof *C);
for( n=0; n<=N; ++n)
{ C[n] = 1;
for( k=n-1; k>0; --k)
{ C[k] += C[k-1];
}
}
return C;
}
在用N = 52调用它之后,例如返回,C [k]将保持C(52,k)为k = 0..52