有更好的方法来实现阶乘吗?
Any better way to implement factorials?
我一直在开发一个库来处理超过 8 字节正常范围的数字 long long(通常)。
我说的是几百到几千位数字。
现在我实现了一个如下所示的阶乘函数:
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 函数,想知道这是否有任何帮助。
虽然很难在没有看到实现的情况下优化代码,但您当然可以通过将递归函数转换为迭代函数来获得一些循环,或者通过 optimizing tail call 帮助编译器为您完成。
largeNum factorial(largeNum& input) {
largeNum res = one;
while (input > one) {
res *= input;
input -= one;
}
return res;
}
当然,这只是计算阶乘的相同 "middle school" 方法的不同实现。如果您正在寻找高级算法,here is a page dedicated to comparing various "hard" implementations.
您可以使用线程来加速计算,使用 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;
}
我不同意所有这些答案,并说典型的迭代和递归阶乘实现对于大输入值来说是幼稚且昂贵的。
更好的方法是使用 gamma function(或者,更好的是,伽玛函数的自然对数)。
这是有效的,因为 gamma(n) = (n-1)!
或 n! = gamma(n+1)
如果将其与记忆化相结合,您将获得一个适用于大参数的高效解决方案。
gamma 的自然对数特别适合评估 combinations and permutations。
我一直在开发一个库来处理超过 8 字节正常范围的数字 long long(通常)。
我说的是几百到几千位数字。
现在我实现了一个如下所示的阶乘函数:
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 函数,想知道这是否有任何帮助。
虽然很难在没有看到实现的情况下优化代码,但您当然可以通过将递归函数转换为迭代函数来获得一些循环,或者通过 optimizing tail call 帮助编译器为您完成。
largeNum factorial(largeNum& input) {
largeNum res = one;
while (input > one) {
res *= input;
input -= one;
}
return res;
}
当然,这只是计算阶乘的相同 "middle school" 方法的不同实现。如果您正在寻找高级算法,here is a page dedicated to comparing various "hard" implementations.
您可以使用线程来加速计算,使用 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;
}
我不同意所有这些答案,并说典型的迭代和递归阶乘实现对于大输入值来说是幼稚且昂贵的。
更好的方法是使用 gamma function(或者,更好的是,伽玛函数的自然对数)。
这是有效的,因为 gamma(n) = (n-1)!
或 n! = gamma(n+1)
如果将其与记忆化相结合,您将获得一个适用于大参数的高效解决方案。
gamma 的自然对数特别适合评估 combinations and permutations。