【问题标题】:Getting factorial of a large number [duplicate]获得大量的阶乘[重复]
【发布时间】:2012-04-13 20:48:40
【问题描述】:

可能重复:
Calculate the factorial of an arbitrarily large number, showing all the digits

这个问题在这里可能会被问一千次。我正在通过修改重复我的问题。我想计算一个大数的阶乘(max range of the number=10^6)。通常我们使用从i=1i=numberfor 循环,并且每次都将旧值乘以新值。这对小数字很好,但如果我有大数字怎么办? for 循环的范围现在增加了。 Java 原始数据类型intlong 无法处理生成的大数字。他们只是溢出。虽然我知道BigInteger 类,它可以处理这么大的输出,但for 循环仍然不适合我。有人可以建议我任何勾号,计算数字阶乘的任何技巧吗?以下是适用于小数字的简单程序-

public long getFactorial(long number) {
    long factorial = 1;
    for (long i = 1; i <= number; ++i) {
        factorial *= i;
    }
    return factorial;
}

【问题讨论】:

  • 1000000!12,815,519 数字长。你需要一个“真正的”bignum 库来处理它。
  • 有基于链表/堆栈的解决方案来实现这一点。如this
  • @Ravi Joshi:也许您应该澄清您是否需要一个精确的(BigInteger)答案,或者是否需要一个近似值。
  • @NedBatchelder:我需要准确的答案。因为接下来会使用这个结果值。
  • 我严重怀疑您是否需要该号码。你可能认为你这样做了,但你很可能错了。这就是你所希望的那样天真的阶乘实现。没有 ln(gamma),没有记忆。你不会走得太远。

标签: java algorithm math factorial


【解决方案1】:

了解该值在 105565708 的范围内。它本身将占用大约 2 兆字节的空间。

也就是说,Guava's BigIntegerMath.factorial(int) 足以应付它,更重要的是,它实际上已经针对大型阶乘进行了优化——它会明显比简单的 @987654324 做得更好@ 环形。 (披露:我为 Guava 做出了贡献......并且我自己写了很多 BigIntegerMath.factorial。)

也就是说,我不会完全称它为快——我的基准测试表明该范围内的阶乘平均为 414 毫秒——但不会有一个真正快速的解决方案,除非非常重载bignum 库,我不希望它们会明显更快。

如果您需要确切的值。如果你能接受对数,那么是的,要么使用 Apache 的 logGamma(n+1) 得到 ln(n!),要么自己近似:

double logFactorial = 0;
for (int i = 2; i <= n; i++) {
  logFactorial += Math.log(i);
}

可能会累积一些舍入误差,但无论如何它应该是一个近似值。

【讨论】:

  • 酷...但我必须先查看Guavafactorial 方法的源代码。我应该知道其中的逻辑。谢谢。
  • @Louis Wasserman:你的意思是 factorial(1_000_000) 需要 414 毫秒,真的吗?在我的电脑上,它需要 200 多秒。我怀疑你的电脑比我的快 500 倍。有什么解释吗?
【解决方案2】:

对于如此大的 n 值,您可以使用称为 Stirling's Formula 的近似函数。

您可以在此处参考我的答案以获取更多详细信息: https://softwareengineering.stackexchange.com/questions/134968/number-of-combinations/134972#134972

【讨论】:

    【解决方案3】:

    使用Gamma function。伽玛(i + 1) = i!

    org.apache.commons.math.special 为 Java 提供它。

    人们通常做的是计算 log(Gamma(i+1)),然后在对数空间中工作(乘法变成加法等)。

    这里有一些其他快速计算阶乘的方法:http://www.luschny.de/math/factorial/FastFactorialFunctions.htm

    【讨论】:

    • 我没有听说过 Gamma 计算阶乘的函数。你能用一个例子详细说明一下吗?谢谢。
    • 结果不适合double。它只是Double.POSITIVE_INFINITY。除非 OP 愿意接受阶乘的对数,否则这不会有太大帮助。
    • @RaviJoshi 再次检查。见第一个等式。
    • @LouisWasserman 阶乘的日志可以很容易地存储在双精度中。
    • 是的,可以存储日志,但是OP并没有表示他对日志感兴趣。不过,我看到你已经添加了一些关于如何使用它的解释。
    【解决方案4】:

    您将BigInteger 用于factorial,而对于循环变量i,您仍然只需要一个longi 只需要上到 1000000。有什么问题?

    【讨论】:

    • 你分析过它的复杂性吗?我会说它是O(n^2)
    • @Mysticial:问题是关于如何完成它,他担心 for 循环的限制。但这不是问题。我不确定您为什么要引入时间复杂度。
    • 我是说去任何大的i 都是不切实际的,因为复杂性是O(n^2)。 Java 的 BigInteger 没有合适的算法将其降低到准线性。
    • result 的长度将是O(n log n),而BigInteger 使用二次乘法算法。我认为它可能比O(n^2)更糟糕
    • 如果你用自下而上的乘法,它会是O(n^2 log(n))——不管乘法算法。要进行次二次运算,您需要将次二次乘法与某种乘积平衡技术相结合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2013-02-19
    相关资源
    最近更新 更多