修改Fibonacci序列生成器时出现意外行为

时间:2014-12-02 20:14:57

标签: java

我一直在玩我的练习4.10的程序,来自 Java的第4章练习10 。这是我的代码:

import acm.program.*;

public class FibonacciSequenceModified extends ConsoleProgram{

public void run(){

    println("This program will display the numbers of the fibonacci sequence that are less than " +
            "10,000");
    int total=0;
    int x=0;
    int y=1;
    println("F0= "+ x);
    println("F1= "+ y);
    for (int num=2; num<=100; num++){
        total=x+y;
        if (total<10000){
        println("F" + num +"= " +total);
        x=y;
        y=total;
        }
    }


}
}

此程序将起作用,F20 = 6765是其最后一行。但是,如果我修改了代码,那么第一个大括号&#34;}&#34;现在在x = y之前;而不是在y = total之后,程序会变得疯狂,并且将显示超过F20 = 6765的巨大负数。任何人都可以向我解释代码试图用这个简单的改动做什么吗?

3 个答案:

答案 0 :(得分:2)

大括号表示if块的结尾,这阻止了在达到10000后计算斐波纳契数的循环。它需要这样做的原因是因为斐波纳契数以指数方式增长,并且最终对于存储的简单32位int来说太大了。

发生的事情是整数溢出。这意味着你的整数很大,不能用32位。

作为示例,考虑最大的32位有符号整数:2,147,483,647

01111111111111111111111111111111

如果你加1,就会变成这样:

10000000000000000000000000000000

这是最小的负数:2,147,483,648。你的数字超出了这个数字。这就是你看到负片的原因


如果你继续“添加”,最终你会得到以下结论:

11111111111111111111111111111111

Two's compliment表示中为-1。如果你加1,你就会得到这个

100000000000000000000000000000000

您可能会注意到,那里有超过32位。最重要的位(1)将被截断,我们将回到0。

00000000000000000000000000000000

你的fibbonacci数字保持超过这个32位的限制,因为最重要的位被关闭,剩下的位不是很有意义,这就是你看到随机数字的原因。

答案 1 :(得分:2)

如果您在}之前移动x=y;,那么您实际上会继续确定超过数字20的Fibonacci数字,但只有在它们小于10,000时才打印出来。这种情况一直有效,直到数字变得如此之大,以至于它们溢出了您用来计算数字的int数据类型。最大int可能是2 31 - 1,第47个数字通过该值。结果是由于这种溢出,其中一些将是负面的。这些负数小于10,000,因此它们被打印出来。

完全删除if及相关大括号后,可以更清楚地看到此行为:

This program will display the numbers of the fibonacci sequence that are less than 10,000

F0= 0
F1= 1
F2= 1
F3= 2
F4= 3
F5= 5
F6= 8
...
F18= 2584
F19= 4181
F20= 6765
F21= 10946
F22= 17711
...
F44= 701408733
F45= 1134903170
F46= 1836311903
F47= -1323752223  <= First negative resulting from overflow.
F48= 512559680
F49= -811192543
F50= -298632863
...
F98= -90618175
F99= -889489150
F100= -980107325

你可以使用代码的原因是,如果总数小于10,000,你只能确定下一个数字,所以你从来没有计算出足够高的数字来溢出。 num为22及以上的循环没有任何作用。

一旦总数大于10,000,如果您}退出循环,则可以x=y;之前保持break

if (total<10000){
    System.out.println("F" + num +"= " +total);
}
else
{
    break;
}
x=y;
y=total;

顺便说一句,请注意,将totalxy的数据类型更改为long会有所帮助,但它只会推迟问题。这将在第93个学期溢出long

F90= 2880067194370816120
F91= 4660046610375530309
F92= 7540113804746346429
F93= -6246583658587674878
F94= 1293530146158671551
F95= -4953053512429003327
F96= -3659523366270331776
F97= -8612576878699335103
F98= 6174643828739884737
F99= -2437933049959450366
F100= 3736710778780434371

BigInteger可以精确地存储这些巨大的数字。

答案 2 :(得分:0)

如果您有以下代码

int total = 0;
int x = 0;
int y = 1;
System.out.println("F0= "+ x);
System.out.println("F1= "+ y);
for (int num = 2; num <= 100; num++){
    total = x + y;

    if (total < 10000) {
        System.out.println("F" + num +"= " +total);
    }

    x = y;
    y = total;
}

正在发生的事情是,该程序成功计算了斐波纳契数字,直到F46,但由于语句if (total < 10000) . . .(在ideone.com中测试),因此仅打印最多F20的数字。第47个斐波纳契数超过int的最大值,因此返回垃圾,这通常被视为负数。你的程序会打印所有这些垃圾,因为它不到10,000。 int的最大值是2^31 - 1,大约是20亿。