计算组合时溢出

时间:2015-06-25 12:16:27

标签: c++

我正在尝试用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;
}

有什么想法解决这个问题?

5 个答案:

答案 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/

https://gmplib.org/

我知道在这个问题上有不同的算法方法。我记得有一些解决方案使用字符串来存储整数表示和东西但是@Konrad提到这可能是一个很糟糕的方法。

答案 2 :(得分:2)

问题是阶乘很快变大。 40!太大而无法存储在long long中。幸运的是,您实际上并不需要在此计算此数字,因为您可以在计算C(n, r)之前减少计算中的分数。这产生了等式(来自Wikipedia):

reduced combination formula

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进行此类数学