【问题标题】:StackOverflowError computing factorial of a BigInteger?StackOverflowError 计算 BigInteger 的阶乘?
【发布时间】:2012-01-24 18:57:55
【问题描述】:

我正在尝试编写一个 Java 程序来计算大数的阶乘。看来BigInteger 无法容纳这么大的数字。

下面是我写的(直截了当的)代码。

 public static BigInteger getFactorial(BigInteger num) {
      if (num.intValue() == 0) return BigInteger.valueOf(1);

      if (num.intValue() == 1) return BigInteger.valueOf(1);

      return num.multiply(getFactorial(num.subtract(BigInteger.valueOf(1))));
  }

上述程序处理的最大数量为 5022,之后程序抛出一个StackOverflowError。有没有其他办法处理?

【问题讨论】:

  • 对于 BigInteger 数据类型来说,这不可能是最大的。 stackoverflow 异常在哪里被抛出?发布更多相关代码。
  • 是的,使用迭代算法。 BigInteger 做得很好,getFactorial 只是吃掉了所有的堆栈空间。
  • @harold (+1) - 另一个我认为递归对教授大学生有害的技术的例子,至少在没有尾递归的语言中是这样。这是一项智力练习,但最终对任何有趣的事情都没有用。
  • 递归“对任何有趣的事情都没有用”?嗯。
  • @JonH:堆栈溢出与数值溢出不同,num 的类型在这里无关。

标签: java algorithm stack-overflow biginteger factorial


【解决方案1】:

这里的问题看起来像是 stack overflow 与过多的 recursion (5000 次递归调用看起来是正确的调用次数来炸毁 Java call stack)而不是 BigInteger 的限制。迭代地重写阶乘函数应该可以解决这个问题。例如:

public static BigInteger factorial(BigInteger n) {
    BigInteger result = BigInteger.ONE;

    while (!n.equals(BigInteger.ZERO)) {
        result = result.multiply(n);
        n = n.subtract(BigInteger.ONE);
    }

    return result;
}

希望这会有所帮助!

【讨论】:

  • 修改代码以使用静态常量BigInteger.ONEZERO
  • 在算法课上,我记得在 Java 中实现极深递归 Ackermann function 时使用 Push/Pop 到堆栈以避免 StackOverflow 异常的技术,特别是对于 A(4,1) 大约需要 28在 Core2Duo T7250 上执行的分钟数。
  • @ee.- 您所描述的是将运行时堆栈替换为显式堆栈。虽然大多数算法的复杂性通常比递归版本高,但这是可以实现的。
  • @templatetypedef 我同意。当使用标准递归方法在A(4,1) 运行Ackermann function 时,Java 中的标准运行时堆栈将立即捕获 StackOverflow 异常。显式堆栈似乎解决了这个问题,尽管显式堆栈方法很复杂。
【解决方案2】:

问题不在于 BigInteger,而在于您使用了递归方法调用 (getFactorial())。

【讨论】:

    【解决方案3】:

    试试这个,一个迭代算法:

    public static BigInteger getFactorial(int num) {
        BigInteger fact = BigInteger.valueOf(1);
        for (int i = 1; i <= num; i++)
            fact = fact.multiply(BigInteger.valueOf(i));
        return fact;
    }
    

    【讨论】:

      【解决方案4】:

      来自 Google 的 Guava 库具有高度优化的阶乘实现,可输出 BigInteger。 Check it out。 (它会进行更平衡的乘法运算并优化掉简单的移位。)

      【讨论】:

        【解决方案5】:

        阶乘的幼稚实现在实际情况下是行不通的。

        如果您有严重的需求,最好的办法是编写一个gamma 函数(或ln(gamma) 函数),它不仅适用于整数,也适用于十进制数。记住结果,这样您就不必使用WeakHashMap 重复计算,您就可以开展业务了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-19
          • 2022-01-17
          • 1970-01-01
          • 2023-03-22
          • 2013-02-19
          • 1970-01-01
          • 1970-01-01
          • 2010-12-17
          相关资源
          最近更新 更多