使用 BigInteger 的斐波那契数列不产生答案
Fibonacci sequence using BigInteger Does not yield an answer
我要找到斐波那契数列的第 100 个元素,我最初尝试使用 int
存储数字值,但它溢出并切换为负值,就像 long
.
然后我遇到了 BigInteger
我确实使用一个简单的 for
循环和一个 array
来存储结果并访问前面的元素得到了解决方案。
现在作为我正在尝试使用 recursion
解决同样的问题 该程序似乎没有终止。我在这里错过了什么吗?或者 BigInteger
不建议与递归一起使用?
代码如下:
import java.math.BigInteger;
class Test {
public static void main(String[] args) {
BigInteger n = BigInteger.valueOf(100);
System.out.println(fib(n));
}
public static BigInteger fib(BigInteger n) {
if (n.compareTo(BigInteger.valueOf(1)) == 0 || n.compareTo(BigInteger.valueOf(1)) == -1)
return n;
return fib(n.subtract(BigInteger.valueOf(1))).add(fib(n.subtract(BigInteger.valueOf(2))));
}
}
在评论中,您提到您认为程序不会终止的假设是基于它 运行 超过 5 分钟的事实。那是不是证明非终止的方式。
如果您在一定时间内观察到程序终止,那么您可以得出结论,它确实终止了。但是,如果您没有观察到它在一定时间内终止,那么您可以准确地说nothing它是否终止。等久一点可能会终止,等久了可能会终止,甚至理论上可能会终止,但比宇宙的热寂还要长。
在您的特定情况下,算法是完全正确的,并且它总是会终止。它根本不是一个非常有效的算法:为了计算 fib(n)
,fib
被调用 fib(n) 次,因为你一遍又一遍地计算相同的数字。
如果我们假设您可以每个时钟周期执行一次 fib
(这是一个乐观的假设,因为对 fib
的一次调用执行一个条件、两次减法、一次加法和两次调用在大多数情况下到 fib
,并且一次添加可能已经需要多个时钟周期,具体取决于 CPU),我们进一步假设您有一个 100 核心 CPU 并且您的代码实际上是并行执行,并且您有 100 CPUs,每个 CPU 的时钟频率为 100 GHz,并且您有一个由 100 台计算机组成的集群,那么它将 still 大约需要一个小时。
在一些更现实的假设下,您的程序完成所花费的时间大约为数万年。
由于您的代码未并行化,为了让您的代码在更现实的 4 GHz CPU 上在 5 分钟内完成,它需要执行 fib
将近 300 百万次每个时钟周期。
对代码的预期性能进行一些非常粗略的猜测通常会有所帮助。如您所见,您不需要成为 Java 或 JVM、编译器、优化或计算机组织或 CPU 设计或性能工程方面的专家。您不需要确切地知道您的代码被编译成什么。您不需要知道一个整数 ADD
需要多少个时钟周期。因为 甚至 当你做出一些 完全过分荒谬的 假设时,你仍然可以很容易地看到你的代码 不可能 在几分钟甚至几小时内完成。
我要找到斐波那契数列的第 100 个元素,我最初尝试使用 int
存储数字值,但它溢出并切换为负值,就像 long
.
然后我遇到了 BigInteger
我确实使用一个简单的 for
循环和一个 array
来存储结果并访问前面的元素得到了解决方案。
现在作为我正在尝试使用 recursion
解决同样的问题 该程序似乎没有终止。我在这里错过了什么吗?或者 BigInteger
不建议与递归一起使用?
代码如下:
import java.math.BigInteger;
class Test {
public static void main(String[] args) {
BigInteger n = BigInteger.valueOf(100);
System.out.println(fib(n));
}
public static BigInteger fib(BigInteger n) {
if (n.compareTo(BigInteger.valueOf(1)) == 0 || n.compareTo(BigInteger.valueOf(1)) == -1)
return n;
return fib(n.subtract(BigInteger.valueOf(1))).add(fib(n.subtract(BigInteger.valueOf(2))));
}
}
在评论中,您提到您认为程序不会终止的假设是基于它 运行 超过 5 分钟的事实。那是不是证明非终止的方式。
如果您在一定时间内观察到程序终止,那么您可以得出结论,它确实终止了。但是,如果您没有观察到它在一定时间内终止,那么您可以准确地说nothing它是否终止。等久一点可能会终止,等久了可能会终止,甚至理论上可能会终止,但比宇宙的热寂还要长。
在您的特定情况下,算法是完全正确的,并且它总是会终止。它根本不是一个非常有效的算法:为了计算 fib(n)
,fib
被调用 fib(n) 次,因为你一遍又一遍地计算相同的数字。
如果我们假设您可以每个时钟周期执行一次 fib
(这是一个乐观的假设,因为对 fib
的一次调用执行一个条件、两次减法、一次加法和两次调用在大多数情况下到 fib
,并且一次添加可能已经需要多个时钟周期,具体取决于 CPU),我们进一步假设您有一个 100 核心 CPU 并且您的代码实际上是并行执行,并且您有 100 CPUs,每个 CPU 的时钟频率为 100 GHz,并且您有一个由 100 台计算机组成的集群,那么它将 still 大约需要一个小时。
在一些更现实的假设下,您的程序完成所花费的时间大约为数万年。
由于您的代码未并行化,为了让您的代码在更现实的 4 GHz CPU 上在 5 分钟内完成,它需要执行 fib
将近 300 百万次每个时钟周期。
对代码的预期性能进行一些非常粗略的猜测通常会有所帮助。如您所见,您不需要成为 Java 或 JVM、编译器、优化或计算机组织或 CPU 设计或性能工程方面的专家。您不需要确切地知道您的代码被编译成什么。您不需要知道一个整数 ADD
需要多少个时钟周期。因为 甚至 当你做出一些 完全过分荒谬的 假设时,你仍然可以很容易地看到你的代码 不可能 在几分钟甚至几小时内完成。