2013-09-27 2 views
5

я пытаюсь некоторые математические-операции с Java, что делает проверить номер, если его (не) даже и изменить его до тех пор, как он получает 1.Почему цикл умирает? (Коллатца гипотеза)

Я пытаюсь запустить мой цикл для 999999times , он, кажется, застревает примерно в ~ 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); 
} 
+8

Просто троллинг: 'for (int i = 2; i <1000000; i ++) {' выполнит 999998 раз ... – ppeterka

+2

Где инициализируются переменные 'counter' и' mostCounter'? – zakinster

+11

Привет. Просить людей обнаружить ошибки в коде не особенно продуктивно. Вы должны использовать отладчик (или добавить заявления печати), чтобы изолировать проблему, отслеживая ход вашей программы и сравнивая ее с тем, что вы ожидаете.Как только двое расходятся, вы нашли свою проблему. (И затем, если необходимо, вы должны построить [минимальный тестовый сценарий] (http://sscce.org).) –

ответ

10

У вас переполнение, потому что 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++; 
} 
+0

останавливается около 120000, и это будет ~ 360000. это уже переполнение? – bofredo

+0

@bofredo просто добавьте Исключение, которое я показал в моем примере, и вы это увидите. Самое высокое, что 'n' может пойти, это 715.827.882 –

+0

Только потому, что ваше начальное значение n равно 120000, не означает, что он не станет больше в цикле while! Если вы возьмете, например, n = 6, последовательность будет 6, 3, 10, 5, 16, 8, 4, 2, 1, так что n станет 16 между ними! (n = 113383 - наименьшее значение n, которое производит int-overflow!) – isnot2bad

1

Хм, ваш код выглядит хорошо. Вы решаете довольно типичную проблему

Является ли n целым? Если это коротко, вы можете переполнить его.

Кроме этого, максимальное значение целых чисел превышает 2 миллиарда, поэтому вы не должны его бить. Только в случае, попробуйте установить п на долго, чтобы увидеть, если это помогает

Edit: Возьмем, к примеру, число 77671 Согласно блоге я прочитал (читай: непроверенные) наивысший п для г = 77671 является 1047216490

Итак, я думаю, что n должно быть длинным, теперь, когда я думаю об этом подробнее

+0

я использовал стандартный int. подумал, что размер подходит для моей проблемы. – bofredo

+2

@Chris Если вы начинаете [последовательность градиента] (http://en.wikipedia.org/wiki/Collatz_conjecture) с '113383', она будет превышать 2 миллиарда на 121-й итерации. Это именно то, что происходит здесь. – zakinster

+0

просто используйте тип данных «long» вместо «int». Все остальное будет таким же – Chris

4

У вас проблема с переполнением. Измените код, как это и вы видите его:

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 он прекрасно работает.

+0

В качестве стартового значения возьмите n = 1431655765. Он будет генерировать неотрицательный переполнение (n * 3 + 1 = 0), и пока он будет работать вечно ... – isnot2bad

+0

его по-прежнему ((Integer.MAX_VALUE -1)/3), поэтому будет выдавать результат. Но да, можно утверждать, что Исключение должно быть брошено во второй строке, а не в первой. –

1

Эта коррекция работы:

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); 
} 
+0

thx. на самом деле достаточно просто иметь n как длинный. – bofredo

0

Вы просто работаете в бесконечный цикл внутри пока блок, добавьте System.out.println(counter); после counter++, чтобы увидеть, что происходит ..

Смежные вопросы