除了正常的递归函数和循环方法之外,找到数字的阶乘的有效方法是什么?由于普通方法产生输出所需的时间太长,有什么方法可以比递归和循环方法降低时间复杂度?如果没有,为什么?
6 回答
从79开始!是 1.711E98 你只需要一个包含 79 个数字的列表就可以使用查找表。阶乘以整数格式列于: http ://www.tsm-resources.com/alists/fact.html 或以科学格式列于http://home.ccil.org/~remlaps/javascript/jstest1.html,所以它只是“剪切和粘贴”
如果要计算许多阶乘值,则可以选择在进行时缓存这些值。
如果你只是一个单一的计算factorial(n)
,那么循环可能是你能得到的最好的。您可以通过一些循环展开(一次计算两个或四个乘法)从处理器中获得更多收益,但它不太可能适用于非常大的阶乘,因为乘法本身就变成了一系列冗长的指令。
据我所知,没有什么“神奇”的数学可以12 * 13 * 14 * 15
比将它们相乘更快地计算出一个或类似的序列。
您可以使用斯特林近似来评估一个大的阶乘。
计算非常大的因子的最简单方法是使用 gamma 函数。另一方面:它不会像查表那样快(正如其他人所建议的那样);如果您使用的是内置类型,则需要以下表格:
for 32 bits: 12 entries
for 64 bits: 20 entries
for float: 34 entries
for double: 170 entries
(上表中可能存在一个错误。我编写了代码以非常快速地计算它们。)
对于double
,并且可能float
,通常的循环方法可能会引入太多的舍入错误。如果您不想使用表格,并且您有 C++11,
exp( lgamma( i + 1 ) )
那么应该这样做。(如果你没有 C++11,你可能有这个lgamma
函数。它在 C99 中。)
如果您正在处理某种扩展范围类型,则可能必须为您的类型实现lgamma
(and exp
)。
不,不是。如果您担心运行时成本,可以使用查找表(这要求您使用预先计算的值)来减少它。
存储每个阶乘可能需要太多内存,所以让我们存储每个 k:th 阶乘。这将要求程序在运行时执行多达 k 次乘法。
假设您的运行时性能要求将每个阶乘限制为 1000 次乘法。然后你需要预先计算并存储 1000!, 2000!, 3000! 依此类推,直到您要支持的上限(可用内存会限制您)。
由于阶乘很快变得比本机数据类型大,因此您需要一个可以处理大量数字的类。我假设存在这样一个类并且它被称为BigInteger
.
BigInteger preCalculatedValues[] = { new BigInteger("1"), new BigInteger("<value of 1000!>"), and so on... }
BigInteger factorial(int n) {
int pre = n/1000;
int i;
if (pre > LARGEST_VALUE_HANDLED) {
// Either throw an exception or let it take longer. I choose to let it take longer.
pre = LARGEST_VALUE_HANDLED;
}
BigInteger result = preCalculatedValues[pre];
for (i = pre * 1000 + 1; i <= n; i++) {
result.multiplyWith(i); // This operation is probably overloaded on the * operator
}
}