我正在尝试用C ++计算组合C(40,20),但是C ++中的数据类型似乎无法正确处理此计算,即使我使用了long long
数据类型。以下是我的代码:
#include <iostream>
long long fac(int x) {
register long long i,f = 1; // Optimize with regFunction
for(i = 1;i <= x;i++)
f *= i;
std::cout << f << std::endl;
return f;
}
// C(n,r) = n!/r!(n-r)!
long long C(long long n, long long r) {
return fac(n) / (fac(r) * fac(n - r));
}
int main(int argc, char const *argv[]) {
std::cout << C(40, 20) << std::endl;
return 0;
}
有什么想法解决这个问题?
答案 0 :(得分:4)
在乘法后立即执行除法,立即计算C:
long long C(long long n, long long r)
{
long long f = 1; // Optimize with regFunction
for(auto i = 0; i < r;i++)
f = (f * (n - i)) / (i + 1);
return f ;
}
结果应该是精确的(没有余数的除法,直到溢出),因为(i + 1)中存在的任何整数因子已经存在于(n -i)中。 (不应该太难证明)
答案 1 :(得分:3)
您的数字增长太多,这是此类计算中的常见问题,我担心没有直接的解决方案。即使您可能会减少一些乘法次数,您可能仍会因long long
你可能想检查一下:
https://mattmccutchen.net/bigint/
我知道在这个问题上有不同的算法方法。我记得有一些解决方案使用字符串来存储整数表示和东西但是@Konrad提到这可能是一个很糟糕的方法。
答案 2 :(得分:2)
问题是阶乘很快变大。 40!太大而无法存储在long long
中。幸运的是,您实际上并不需要在此计算此数字,因为您可以在计算C(n, r)
之前减少计算中的分数。这产生了等式(来自Wikipedia):
自 k 以来效果更好! ( r !在你的代码中)是一个比 n !小得多的数字。但是,在某些时候它也会崩溃。
或者,您也可以通过实现递归算法来使用递归定义。但是,除非您记忆中间结果,否则这将是非常效率低(指数运行时间)。
答案 3 :(得分:2)
一种懒惰的方法是使用支持多种精度的库,例如GNU GMP。
正确安装后(可从大多数Linux发行版的存储库获得),可归结为:
#include <gmpxx.h>
添加到源文件long long
替换为mpz_class
-lgmpxx -lgmp
来源:
#include <iostream>
#include <gmpxx.h>
mpz_class fac(mpz_class x) {
int i;
mpz_class f(1); // Optimize with regFunction
for(i = 1;i <= x;i++)
f *= i;
std::cout << f << std::endl;
return f;
}
// C(n,r) = n!/r!(n-r)!
mpz_class C(mpz_class n, mpz_class r) {
return fac(n) / (fac(r) * fac(n - r));
}
int main(int argc, char const *argv[]) {
std::cout << C(40, 20) << std::endl;
return 0;
}
编译并运行:
$ g++ comb.cpp -lgmpxx -lgmp -o comb
$ ./comb
2432902008176640000
2432902008176640000
815915283247897734345611269596115894272000000000
137846528820
如果你想要彻底,你可以做更多,但这会得到答案。
答案 4 :(得分:0)
即使您使用了uint64又名ulonglong,最大值为18446744073709551615
而40!是815915283247897734345611269596115894272000000000
,有点更大。
我建议您使用GMP进行此类数学