3

Javaでハノイの塔の問題をテストしていたところ、次のコードを実行しました:(便宜上削除しました)sysouts

public class Util {

    public static void main(String[] args) {
        for (int i = 1; i <= 30; i++) {
            long startTime = System.currentTimeMillis();
            solveTowerOfHanoi(i, "A", "B", "C");
            System.out.println("Time taken for " + i + ": "
                    + (System.currentTimeMillis() - startTime));
        }
    }

    public static void solveTowerOfHanoi(int n, String src, String inter,
            String dest) {
        if (n == 0) {
            return;
        }
        solveTowerOfHanoi(n - 1, src, dest, inter);
        solveTowerOfHanoi(n - 1, inter, src, dest);
    }
}

私は実験を行い、1 から 35 までのディスク サイズ (インデックス) を試しました。非常に奇妙なタイミング パターンを観察しました。プログラムの出力は次のとおりです。

Time taken for 1: 0
Time taken for 2: 0
Time taken for 3: 0
Time taken for 4: 0
Time taken for 5: 0
Time taken for 6: 0
Time taken for 7: 0
Time taken for 8: 1
Time taken for 9: 0
Time taken for 10: 0
Time taken for 11: 0
Time taken for 12: 0
Time taken for 13: 0
Time taken for 14: 0
Time taken for 15: 0
Time taken for 16: 0
Time taken for 17: 0
Time taken for 18: 3
Time taken for 19: 2
Time taken for 20: 11
Time taken for 21: 10
Time taken for 22: 39
Time taken for 23: 37
Time taken for 24: 158
Time taken for 25: 147
Time taken for 26: 603
Time taken for 27: 579
Time taken for 28: 2414
Time taken for 29: 2304
Time taken for 30: 9509
Time taken for 31: 9408
Time taken for 32: 38566
Time taken for 33: 37531
Time taken for 34: 152255
Time taken for 35: 148704

質問 1 : アルゴリズムには指数関数的な増加(2^n-1)があります。しかし、その後、20 から 35 へと急上昇したのでしょうか。

質問 2 : また、私をさらに驚かせたもう 1 つのことは、ペアの時間が等しいことです。19 から始めて、(19,20)、(21,22)、(23,24)、(25,26) など....同等の時間があります。アルゴリズムの成長率が実際に指数関数的である場合、なぜ2つのインデックスがほぼ同等の時間を与え、次のインデックスで突然ジャンプするのか理解できませんか?

: このプログラムを 2 ~ 3 回繰り返したところ、ほぼ同等のタイミングが得られたので、平均的な実行と見なすことができます。

編集
して、次の 結果System.nanoTime()を得ました:

Time taken for 1: 62644
Time taken for 2: 3500
Time taken for 3: 3500
Time taken for 4: 4200
Time taken for 5: 6300
Time taken for 6: 7350
Time taken for 7: 11549
Time taken for 8: 19948
Time taken for 9: 47245
Time taken for 10: 73142
Time taken for 11: 87491
Time taken for 12: 40246
Time taken for 13: 39196
Time taken for 14: 156784
Time taken for 15: 249875
Time taken for 16: 593541
Time taken for 17: 577092
Time taken for 18: 2318166
Time taken for 19: 2305217
Time taken for 20: 9468995
Time taken for 21: 9082284
Time taken for 22: 37747543
Time taken for 23: 37230646
Time taken for 24: 150416580
Time taken for 25: 145795297
Time taken for 26: 603730414
Time taken for 27: 578825875
Time taken for 28: 2409932558
Time taken for 29: 2399318129
Time taken for 30: 9777009489

出力はミリ秒にほぼ似ていますが、全体像を明確にしています...私の質問 1の答えかもしれませんが、質問 2はまだ興味深いものです。そして、System.nanoTime()もう1つの質問を提起しました:

質問 3 : インデックス 1 が次のインデックス (2、3...) などよりも時間がかかるのはなぜですか?

4

2 に答える 2

2

回答 1: 18 枚のディスクでプレイを開始するまでは、タイミング測定値が粗すぎて有用な情報を表示できません。この値の範囲内のディスク数に関して、実行時間の変化について結論を下すのは賢明ではありません。

回答 2: ハノイの塔の問題は十分に研究されており、その時間計算量はよく知られているため、時間計算量がより優れた実装を見つけることはまずありません。したがって、プログラムの連続実行で実行時間がほぼ等しくなる実装に固有の何かがあると結論付けることができます。

あなたの Java に問題があることはすぐにはわかりません。そのため、あなたのシステムから返されるタイミングに何か奇妙な点があるという結論に飛びつきます。

編集

ナノタイミングは、ミリタイミングと同様の構造を示します。ただし、27 ディスクと 28 ディスクのミリタイミングは類似しており、その後に 29 ディスクへの大きなジャンプが続くのに対し、ナノタイミングでは 27 ディスクから 28 ディスクへの大きなジャンプがあり、これは次のようになります。 29 ディスクのタイミング。

新しい質問 3 については、メイン ループを 2 回実行し、最初の実行の結果を破棄して、OP が JIT と JVM を「スピンアップ」することをお勧めします。System.out.printlnへの呼び出しの外で、次のような終了時刻も取得すると思います。

long startTime = System.currentTimeMillis();
solveTowerOfHanoi(i, "A", "B", "C");
long endTime = System.currentTimeMillis();
System.out.println("Time taken for " + i + ": " + (endTime - startTime));

編集2

OPさんのコメントを受けて…

1 つのディスクを解決するのに 2 つのディスクよりも時間がかかる一般的な説明の 1 つは、あなたのタイミングには舞台裏の「スタートアップ関連」が含まれているということです。ランタイム システムは、バイトコードをマシン コードに変換します。あなたは (Java の達人はここで軽蔑をぶつけても構いません) その 2 番目の翻訳のタイミングを計っているかもしれません。JIT に関して言えば、一般的なアプローチは、ジャストインタイム コンパイラーは、コードの動的分析が必要であると示唆した場合にのみ作動するというものです。2 回目のラウンドでのみコードを最適化する可能性があります。

編集3

OK、私は餌を取り、コードの2つのバージョンの時間を計りましnanoTimecurrentTimeMillis. 私はそれを結論付けます

  1. 私が最初に主張したように、OP のシステムには怪しいところがあります。列 2 に示したタイミングは予想に非常に近いので、これは一般的な Java の問題ではありません。
  2. nanoTime私のマシンでの の実装、またはおそらくそれについての私の理解に深刻な問題があります。これは、私の最初の主張の背後にある考え方に重みを加えます。コンピューターの時計に惑わされるのは簡単であり、それらが提供する結果は常に額面どおりに受け取ることはできません.

また、時間測定を開始する前に JIT/JVM のスピンアップをテストしましたが、最終的にはほとんど影響がなかったので、そのデータはここには含めていません。

| Discs | milliS | nanoS      |
|    1: |      0 |       7000 |
|    2: |      0 |       4000 |
|    3: |      0 |       6000 |
|    4: |      0 |      11000 |
|    5: |      0 |      22000 |
|    6: |      0 |      43000 |
|    7: |      0 |      84000 |
|    8: |      0 |     172000 |
|    9: |      1 |     331000 |
|   10: |      0 |     663000 |
|   11: |      2 |    1334000 |
|   12: |      2 |    2632000 |
|   13: |      6 |    5265000 |
|   14: |     11 |   10476000 |
|   15: |     21 |   22034000 |
|   16: |     42 |   43407000 |
|   17: |     85 |   89683000 |
|   18: |    169 |  171209000 |
|   19: |    337 | -655065000 |
|   20: |    673 |  688203000 |
|   21: |   1345 | -814465000 |
|   22: |   2708 |  707742000 |
|   23: |   5531 | -533716000 |
|   24: |  10918 | -140615000 |
|   25: |  21542 |  628928000 |
|   26: |  42889 |   97892000 |
|   27: |  85698 |  325197000 |
|   28: | 172370 |  -80280000 |
|   29: | 345650 |  110795000 |
|   30: | 685006 |  453388000 |

ノート:

javac -v => Eclipse Java Compiler v_677_R32x, 3.2.1 release, Copyright IBM Corp 2000, 2006. All rights reserved.

java --version => java version "1.4.2"
gij (GNU libgcj) version 4.1.2 20071124 (Red Hat 4.1.2-42)
于 2012-06-26T09:53:30.360 に答える
0

プログラムの複雑さの分析について話すとき、それは漸近的に複雑さを研究することを意味します。指数関数的成長や平等などの他の興味深い機能を結論付けたタイミングデータを考慮する限り、データがあり、それをしばらく見つめると、興味深いパターンが得られると簡単に言えます:)

したがって、Tower Of Hanoi問題の理論的な複雑さに関するタイミングデータからの結論は、ごみになります。

ただし、コードが実際にそのように観察された指数関数的成長を示している理由に興味がある場合は、Javaが使用しているRAMの量に起因する可能性があります。

于 2012-06-26T09:52:03.433 に答える