我有一个方法,它取n并返回第n个Fibonacci数。在方法实现中,我使用BigDecimal
来获取第n个Fibonacci数,然后我使用方法toBigInteger()
将数字作为BigInteger
对象获取,这肯定是因为我正在处理我的大数字应用
我一直得到正确的结果,直到我通过 1475 作为我的方法的参数。在这种情况下,我得到NumberFormatException: Infinite or NaN
,没有任何明确的理由。
请您解释一下我为什么会遇到此异常?
这是我的方法:
BigInteger getFib(int n){
double phi = (1 + Math.sqrt(5))/2;
double squareRoot = (Math.sqrt(5)) + (1/2);
BigDecimal bd = new BigDecimal(Math.floor(Math.pow(phi, n)/(squareRoot)));
return bd.toBigInteger();
}
答案 0 :(得分:5)
你的Math.pow(phi, n)
太大(Infinity),double无法存储它,请改用BigDecimal。
流动怎么样:
static BigInteger getFib(int n) {
BigDecimal x1 = new BigDecimal((1 + Math.sqrt(5)) / 2);
BigDecimal x2 = new BigDecimal((1 - Math.sqrt(5)) / 2);
return x1.pow(n).subtract(x2.pow(n))
.divide(new BigDecimal(Math.sqrt(5))).toBigInteger();
}
来自公式:
<强>更新强>
上面的方法是不正确的,因为Math.sqrt(5)没有足够的精度,如评论所说。我尝试使用Netown的方法更精确地计算sqrt(5),并发现x1.pow(n).subtract(x2.pow(n)).divide(...)
非常耗时,在我的计算机中n = 200时大约30秒。
我认为缓存的递归方式更快:
public static void main(String[] args) {
long start = System.nanoTime();
System.out.println(fib(2000));
long end = System.nanoTime();
System.out.println("elapsed:"+ (TimeUnit.NANOSECONDS.toMillis(end - start)) + " ms");
}
private static Map<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();
public static BigInteger fib(int n) {
BigInteger bi = cache.get(n);
if (bi != null) {
return bi;
}
if (n <= 1) {
return BigInteger.valueOf(n);
} else {
bi = fib(n - 1).add(fib(n - 2));
cache.put(n, bi);
return bi;
}
}
我的电脑花了7毫秒,n = 2000。
答案 1 :(得分:1)
你的问题在这里:
BigDecimal bd = new BigDecimal(Math.floor(Math.pow(phi, n)/(squareRoot)));
Math.floor(Math.pow(phi, n)/(squareRoot))
的结果是给你无限或NaN。
根据BigDecimal javadoc构造函数(BigDecimal(double)
)如果使用值为infinite或NaN的double,则可以抛出NumberFormatException
答案 2 :(得分:1)
这不是INF / NaN的原因,但绝对是错误的。这......
double squareRoot = (Math.sqrt(5)) + (1/2);
......相当于......
double squareRoot = Math.sqrt(5));
...因为(1/2)
是一个整数除法,返回一个整数值;即零。
事实上,我认为INF / NaN最可能的解释是“phi 1475 ”太大而无法表示为double
。因此pow
方法返回INF
...这就是“太大”在Java中表示为浮点数的方式。
如果您想以这种方式计算斐波纳契数,则需要使用能够表示所涉及的真正大数的表示...并以足够的精度表示它们。 Java double
类型不能这样做。事实上,使用BigDecimal
很难进行计算......因为对已接受答案的评论显示了!
我建议使用递归关系。它会变得更简单......也可能更有效率。
答案 3 :(得分:0)
使用float或double创建BigDecimal
不是一个好主意,因为它再次限制了它们的范围
你必须首先创建一个BigDecimal并使用它的函数进行一些操作:
BigDecimal a;
BigDecimal b;
x1.pow(b);