有没有更好的方法来实现阶乘?

时间:2017-04-18 15:22:07

标签: c++ math optimization factorial

我一直在处理一个库来处理超过8字节正常范围的数字(通常)。

我说几百到几千个数字。

现在我已经为这样的阶乘实现了一个函数:

largeNum factorial(largeNum& input) {
    if (input > one) return (input * factorial(input-one));
    else return one;
}

现在这给了我很好的结果。 100!花了大约5秒来计算并且已经超过150位数。结果是正确的。

虽然5秒是很长时间,但200已经花费分钟来计算。

例如,WolframAlpha可以计算出100000!在不到10秒的时间内。

所以必须有更好的方法来做到这一点。我一直在寻找 https://en.wikipedia.org/wiki/Factorial用于所谓的Gamma功能,并想知道这是否会有所帮助。

3 个答案:

答案 0 :(得分:3)

虽然在没有看到实现的情况下很难优化代码,但你可以通过将递归函数转换为迭代函数或者通过optimizing tail call帮助编译器为你完成它来获取一些周期。

largeNum factorial(largeNum& input) {
    largeNum res = one;    
    while (input > one) {
        res *= input;
        input -= one;
    }
    return res;
}

当然,这只是同一个"中学的一个不同的实施"计算因子的方法。如果您正在寻找高级算法here is a page dedicated to comparing various "hard" implementations

答案 1 :(得分:2)

我不同意所有这些答案,并说典型的迭代和递归因子实现对于大输入值来说是天真且昂贵的。

更好的方法是使用gamma function(或者,更好的是,伽玛函数的自然对数)。

这可行,因为gamma(n) = (n-1)!n! = gamma(n+1)

如果将此与memoization结合使用,您就拥有了一个适用于大型参数的高效解决方案。

伽玛的自然对数特别适合评估combinations and permutations

答案 2 :(得分:1)

您可以使用线程来加速计算,使用unix pthread或C ++ std :: thread,它也是跨平台的。只有当数字很大时才会有性能提升,否则它不足以抵消创建线程的成本。

修改:此程序使用四个线程来计算阶乘。

运行程序8次后,平均线程因子时间为14秒,平均非线性因子时间为18秒。
示例程序:

#include <iostream>
#include <thread>
#include <chrono>
#include "BigInt.h"

void fact(int upper, int lower, BigInt& val)
{
    for (auto i = upper; i >= lower; i--)
    {
        val = val*i;
    }
}

int main()
{
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

    int n = 1000;
    BigInt val1("1"), val2("1"), val3("1"), val4("1");

    std::thread thr1(&fact, n, (3*n)/4, std::ref(val1));
    std::thread thr2(&fact, ((3 * n) / 4) - 1, n/2, std::ref(val2));
    std::thread thr3(&fact, (n / 2)-1, n/4,  std::ref(val3));
    std::thread thr4(&fact, (n/4)-1, 1, std::ref(val4));

    thr1.join();
    thr2.join();
    thr3.join();
    thr4.join();
    auto ans = val1*val2*val3*val4;

    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count();
    std::cout << "threaded factorial time: " << duration << "\n";

    t1 = std::chrono::high_resolution_clock::now();
    BigInt ans2("1");
    fact(n, 1, std::ref(ans2));
    t2 = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count();
    std::cout << "non threaded factorial time: " << duration;

    return 0;
}