web-dev-qa-db-ja.com

System.out.printlnが使用されない限り、一見無限ループが終了します

xは常に成長し、常にjよりも大きいため、無限ループになる想定の単純なコードがありました。

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
   x = x + y;
}
System.out.println(y);

しかし、そのままでは、yを出力し、無限にループしません。理由がわかりません。ただし、次の方法でコードを調整すると:

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
    x = x + y;
    System.out.println(y);
}
System.out.println(y);

それは無限ループになり、その理由はわかりません。 Javaは無限ループを認識し、最初の状況ではそれをスキップしますが、2番目の状況でメソッド呼び出しを実行する必要があるため、期待どおりに動作しますか?

87
Omar

どちらの例も無限ではありません。

問題は、Java(または他のほとんどの共通言語))のint型の制限です。xの値が0x7fffffffに達すると、正の値を追加するとオーバーフローが発生し、xが負になるため、jより小さくなります。

最初のループと2番目のループの違いは、内部コードの時間がかかり、xがオーバーフローするまでおそらく数分かかることです。最初の例では、2秒未満で済む場合があります。ほとんどの場合、効果がないため、オプティマイザーによってコードが削除されます。

議論で述べたように、時間はOSが出力をバッファリングする方法、ターミナルエミュレータなどに出力するかどうかに大きく依存するため、数分よりもはるかに長くなる可能性があります。

これらはintとして宣言されているため、最大値に達すると、x値が負になるとループが中断します。

ただし、System.out.printlnがループに追加されると、実行速度が表示されます(コンソールへの出力により実行速度が遅くなるため)。ただし、2番目のプログラム(ループ内にsysoを含むプログラム)を十分に長く実行させると、最初のプログラム(ループ内にsysoを含まないプログラム)と同じ動作になるはずです。

33
Ace Zachary

これには2つの理由があります。

  1. Javaはforループを最適化し、ループの後はxを使用しないため、単純にループを削除します。ループの後にSystem.out.println(x);ステートメントを置くことでこれを確認できます。

  2. Javaは実際にループを最適化しておらず、プログラムを正しく実行しているため、xintおよびオーバーフローに対して大きくなりすぎる可能性があります。整数オーバーフローは、おそらく整数xをjよりも小さい負にするため、ループから出てyの値を出力します。 System.out.println(x);ループの後。

また、最初のケースでも最終的にオーバーフローが発生し、2番目のケースにレンダリングされるため、真の無限ループになることはありません。

13
Ashok Vishnoi

xの値が_2,147,483,647_(intの最大値)を超えると、xが負になり、以下になるため、有限ループです。 yを印刷するかどうかに関係なく、jはもう。

yの値を_100000_に変更し、ループでyを出力するだけで、ループはすぐに中断します。

無限になったと感じる理由は、System.out.println(y);が、アクションを実行しない場合よりもコードの実行を非常に遅くしたためです。

1
Joe Cheng

これらは両方とも無限ループではなく、最初はj = 0であり、j <xの場合、jは整数(j ++)であり、jは整数であるため、ループは最大値に達するまで実行され、オーバーフロー(整数オーバーフローが条件乗算や加算などの算術演算の結果が、格納に使用される整数型の最大サイズを超えると発生します。 2番目の例では、システムはループが中断するまでyの値を出力するだけです。

無限ループの例を探している場合は、次のようになります

int x = 6;

for (int i = 0; x < 10; i++) {
System.out.println("Still Looping");
}

(x)が値10に達することはないためです。

また、二重forループを使用して無限ループを作成することもできます。

int i ;

  for (i = 0; i <= 10; i++) {
      for (i = 0; i <= 5; i++){
         System.out.println("Repeat");   
      }
 }

最初のforループはi <10であるため、このループは無限です。これはtrueであるため、2番目のforループに入り、2番目のforループは== 5になるまで(i)の値を増やします。 i <10であるため、再びforループを実行します

1
Kennedy

興味深い問題実際に両方のケースでループは無限ではありません

ただし、それらの主な違いは、終了するタイミングと、x2,147,483,647である最大int値を超えてからオーバーフロー状態とループに達するまでの時間です。終了します。

この問題を理解する最良の方法は、簡単な例をテストし、その結果を保存することです。

for(int i = 10; i > 0; i++) {}
System.out.println("finished!");

出力:

finished!
BUILD SUCCESSFUL (total time: 0 seconds)

この無限ループをテストした後、終了するのに1秒もかかりません。

for(int i = 10; i > 0; i++) {
    System.out.println("infinite: " + i);
}
System.out.println("finished!");

出力:

infinite: 314572809
infinite: 314572810
infinite: 314572811
.
.
.
infinite: 2147483644
infinite: 2147483645
infinite: 2147483646
infinite: 2147483647
finished!
BUILD SUCCESSFUL (total time: 486 minutes 25 seconds)

このテストケースでは、プログラムの終了と終了にかかる時間に大きな違いがあることに気付くでしょう。

忍耐がない場合、このループは無限であり、終了しないと思われますが、実際には、終了してi値でオーバーフロー状態に達するまでに数時間かかります。

最後に、forループ内にprintステートメントを配置した後、printステートメントのない最初の場合のループよりもはるかに時間がかかると結論付けました。

プログラムの実行にかかる時間は、コンピューターの特定の処理能力(プロセッサー容量)、オペレーティングシステム、およびプログラムをコンパイルしているIDE)の仕様によって異なります。

このケースをテストします:

Lenovo 2.7 GHz Intel Core i5

OS:Windows 8.1 64x

IDE:NetBeans 8.2

プログラムを完了するには、約8時間(486分)かかります。

また、forループi = i + 1のステップの増分は、最大int値に到達するのに非常に遅い要因であることがわかります。

この係数を変更し、より短い時間でループをテストするために、ステップの増分をより速くすることができます。

i = i * 10を入れてテストする場合:

for(int i = 10; i > 0; i*=10) {
           System.out.println("infinite: " + i);
}
     System.out.println("finished!");

出力:

infinite: 100000
infinite: 1000000
infinite: 10000000
infinite: 100000000
infinite: 1000000000
infinite: 1410065408
infinite: 1215752192
finished!
BUILD SUCCESSFUL (total time: 0 seconds)

ご覧のとおり、前のループと比較すると非常に高速です

プログラムの終了と実行の完了には1秒もかかりません。

このテスト例の後、問題を明確にし、 Zbynek Vyskovsky-kvr000's answer の妥当性を証明する必要があると思います。また、この question

0
Oghli