【问题标题】:why does the loop die? ( Collatz conjecture )为什么循环会死? (科拉兹猜想)
【发布时间】:2013-09-27 14:06:48
【问题描述】:

我正在尝试用 java 进行一些数学运算,如果它(非)偶数,它确实会测试一个数字,并且只要它达到 1 就会改变它。

我尝试运行循环 999999 次,它似乎卡在了大约 120000 次左右。好吧,它并没有因为异常而停止,只是感觉编译器卡住了。

我对 Java 不太擅长,谁能解释一下这里发生了什么?

public static void main(String[] args) {

    int n = 0;
    int highestNumber = 0;
    int highestCounter = 0;
    int counter = 0;
    for (int i = 2;i<1000000;i++) {

        if (i%10000==0) {
            System.out.println(i);
        }
        n = i;
        while (n!=1) {
            if (n%2==0) {   
                n = n/2;
            } else {    
                n=3*n+1;
            }
            counter++;
        }
        if (counter>highestCounter) {

            highestCounter = counter;
            highestNumber = i;
            System.out.println("HIGHEST "+highestNumber+" | counter = "+counter);   
        }
        counter = 0;
        n = 0;
    }
    System.out.println("final "+highestNumber);  
}

【问题讨论】:

  • 只是拖钓:for (int i = 2;i&lt;1000000;i++) { 将执行 999998 次...
  • 变量counterhighestCounter在哪里初始化?
  • 嗨。要求人们发现代码中的错误并不是特别有效。您应该使用调试器(或添加打印语句)来隔离问题,方法是跟踪程序的进度,并将其与您期望发生的情况进行比较。一旦两者发生分歧,那么您就发现了您的问题。 (然后如果有必要,你应该构造一个minimal test-case。)
  • 编译器不会卡住,因为你可以运行代码
  • 也许你已经找到了科拉茨猜想的第一个反例! ;)

标签: java loops math collatz


【解决方案1】:

因为3 * n + 1 变得大于Integer.MAX_VALUE,所以出现了溢出。所以n 变为负数,while 循环永远不会停止。

使用long 代替int 代替n

如果您想改为检查溢出:

while (n != 1) {
    if (n % 2 == 0) {
        n = n / 2;
    } else {
        if (n > (Integer.MAX_VALUE - 1) / 3) {
            throw new RuntimeException("overflow!");
        }
        n = 3 * n + 1;
    }
    counter++;
}

Java 8 新增功能

从 Java 8 开始,Math 类为“精确”算术(加法、减法、乘法、除法)提供了额外的静态方法,在发生溢出时会抛出 ArithmeticException。使用这些方法,可以简化代码:

while (n != 1) {
    if (n % 2 == 0) {
        n = n / 2;
    } else {
        n = Math.addExact(Math.multiplyExact(3, n), 1);
    }
    counter++;
}

【讨论】:

  • 它在 120000 左右停止,即约为 360000。是不是已经溢出来了?
  • @bofredo 只需添加我在示例中显示的异常,您就会看到它。 n 最高可以到 715.827.882
  • 只有,因为你的 n 初始值为 120000 并不意味着它在 while 循环中不会变大!如果以 n=6 为例,则序列将是 6、3、10、5、16、8、4、2、1,因此 n 在其间变为 16! (n = 113383 是产生 int 溢出的最小 n!)
  • @Angelo 我试过了,你似乎是对的!现在将尝试使用 long 而不是 int 。谢谢 :)
  • 确实如此。它只是将 n 从 int 更改为 long。 thx 问题已解决,希望吸取教训!
【解决方案2】:

您遇到了溢出问题。像这样更改代码,您会看到它:

    while (n!=1) {
        if(n < 0) throw new IllegalStateException("n should not become < 0" + n + "-" + counter);
        if(n > ((Integer.MAX_VALUE -1) / 3)) System.out.println("n too large. " + n);
        if (n%2==0) {   
            n = n/2;
        } else {    
            n=3*n+1;
        }
        counter++;
    }

如果您将n 设置为long,它可以正常工作。

【讨论】:

  • 以n = 1431655765为起始值。它将产生一个非负溢出(n*3+1 = 0),while 将永远运行......
  • 它仍然结束 ((Integer.MAX_VALUE -1) / 3) 所以会产生输出。但是,是的,有人可能会争辩说应该在第二行而不是第一行抛出异常。
【解决方案3】:

此修正有效:

public static void main(String []args){
    long highestCounter = -1;
    long highestNumber = -1;
    for (long i = 2;i<1000000;i++) {

        if (i%1000==0) {
            System.out.println(i);
        }
        long n = i;
        long counter = 0;
        while (n!=1) {
            if (n%2==0) {   
                n = n/2;
            } else {    
                n=3*n+1;
            }
            counter++;
        }
        if (counter>highestCounter) {

            highestCounter = counter;
            highestNumber = i;
            System.out.println("HIGHEST "+highestNumber+" | counter = "+counter);   
        }
        counter = 0;
        n = 0;
    }
    System.out.println("final "+highestNumber); 
}

【讨论】:

    【解决方案4】:

    嗯,您的代码对我来说看起来不错。你正在解决一个非常典型的问题

    n 是整数吗?如果它很短,你可能会溢出它。

    除此之外,一个整数的最大值超过 20 亿,所以你不应该碰到它。以防万一,尝试将 n 设置为 long 看看是否有帮助

    编辑:例如,数字 77671 根据我阅读的博客(阅读:未经测试),i = 77671 的最高 n 是 1,047,216,490

    所以我认为 n 应该很长,现在我想多了

    【讨论】:

    • 我使用了常规的 int。认为尺寸适合我的问题。
    • @Chris 如果以113383 开头hailstone sequence,在第121 次迭代时将超过20 亿。这正是这里发生的事情。
    • 只需使用数据类型“long”而不是“int”。其他一切都一样
    • @zakinster 完美答案 10/10 !
    【解决方案5】:

    你只需在 while 块内运行一个无限循环,在 counter++ 之后添加 System.out.println(counter); 以查看发生了什么......

    【讨论】:

      猜你喜欢
      • 2019-01-03
      • 2021-11-22
      • 2011-01-24
      • 2012-10-22
      • 2017-06-28
      • 1970-01-01
      • 1970-01-01
      • 2021-05-27
      • 2022-07-12
      相关资源
      最近更新 更多