• ベストアンサー

ダイクストラ法の実行時間

ダイクストラ法の実行時間が, O(n)なのは様々なサイトを見ていると分かるのですが, なぜ,O(n^2)なのかが分かりません. それと,ヒープを使った際に, O((n+m)log(n))になる理由がわかりません. どなたか,分かりやすい解説をお願いします.

質問者が選んだベストアンサー

  • ベストアンサー
  • noocyte
  • ベストアンサー率58% (171/291)
回答No.1

nは節点の数,mは辺の数ですか? Dijkstra のアルゴリズムの主要部分は,次の2つの繰り返し. (1) 最短路が確定していない節点の集合Qの中から,   始点sに最も近い節点Pを1つ選んで取り出す. (2) Pの隣接点の最短距離を更新. Qの初期値は,全節点から始点sを除いたものなので, 全節点数をnとするとその要素数はn-1. したがって上記のループ回数はn-1回で, Qの要素数qはn-1,n-2,…,2,1と変化する. (1) の最小要素を求める処理を単純な全数比較で行う場合,   q-1回の比較が必要.これを全ループ回数分合計すると    Σ{q=1,n-1} (q-1) = (1/2) (n^2 - 3 * n + 2) 回の比較 → O(n^2) (2) の処理時間は,Pの隣接点の個数,つまりPから出る辺の数に比例する.   これを全ループ回数分合計すると,グラフのすべての辺について1回ずつ   処理することになるので,処理時間は辺の総数mに比例する.   つまり O(m).辺の総数が最大になるのは完全グラフの場合で,   m≦nC2= n * (n-1) / 2.したがって O(m)≦O(n^2). したがって Dijkstra のアルゴリズムの処理時間は (1) と (2) を合計して, O(n^2 + m) ≦ O(n^2 + n^2) = O(n^2). Qを始点からの距離で順序付けたヒープで管理する場合は,最小値を見つける 手間は O(1) だが,それを取り出す (削除する) のに O(log(n)) の手間がかかる. これを約n回繰り返すので合計 O(n * log(n)). また (2) において,各節点の距離を更新する場合,ヒープから一旦削除 (O(log(n)) して再登録 (O(log(n)) する必要があるので1回あたり O(log(n)) + O(log(n)) = O(log(n)). これを辺の数だけ繰り返すので O(m * log(n)). 以上を合計すると, O(n * log(n)) + O(m * log(n)) = O((m + n) * log(n)). … 合ってるかな? まあ,↓アルゴリズムのデモでも見ながら考えてください.(笑) Algorithm Demo (最短路問題) http://www-or.amp.i.kyoto-u.ac.jp/research/demo/SP.html

to_koto
質問者

お礼

とても丁寧な返答,ありがとうございます. 参考意見となっていますが,ものすごく参考になります! 助かりました! これで,なんとかなりそう・・・です.

関連するQ&A

  • 2分探索法 『成功・不成功探索一回の実行時間を求める』

    C言語の授業で課題が出たのですが、自分が思っているような値が出なくて困っています。よろしければヒントだけでも頂ければなと思います。 ----------課題---------- 整列されているN個のデータに対して、その中にvがあるかどうかを判断する2分探索プログラムを実行する Nの値を10^6、5×10^6、10^7、5×10^7、10^8 と変化させたとき、成功探索、不成功探索一回の実行時間をそれぞれ求めよ。 このとき、Nと実行時間の関係はどのようになっているか(100字程度で)   N  成功探索  不成功探索  10^6  ***秒   ***秒 5×10^6  ***秒   ***秒  10^7  ***秒   ***秒 5×10^7  ***秒   ***秒  10^8  ***秒   ***秒 ---------------------------------------- ~↑を元に作成したプログラム(成功探索の場合)~ #include <stdio.h> #include <stdlib.h> #include <time.h> #define T 1000000 #define N 100000000 int a[N]; int main(void) { clock_t t1,t2; int t; int i; int l, r, k, v, m; for (i = 0; i < N; i++) a[i] = i; t1=clock(); for (t = 0; t < T; t++) { v = rand() % N; l=0; r=N-1; k=-1; while (r >= l) { m = (l+r)/2; if (v == a[m]) { k=m; break; } if (v < a[m]) r = m-1; else l = m+1; } } t2=clock(); printf("cpu time=%10.6f[micro sec]\n",(double)(t2-t1)*1000000/(CLOCKS_PER_SEC*T)); return 0; } ↑a[i]すべてに0~N-1を代入し、vにランダムに0~N-1の値を代入する。2分探索法で、v=a[i]となったら終了する。 実行時間は、↑の操作を10^6回行い、その平均を実行時間をする…単位:マイクロ秒 として、コンパイルして動いたんですが実行時間の値にずれがありどの値が一番適切か分からなくて困っています。 ↑のプログラムをそれぞれのNで実行したところ N=  10^6で実行・・・1.016 0.891 0.969 1.155 1.14 [micro sec] 5×10^6で実行・・・1.422 1.500 1.563 1.250 1.406 1.297  10^7で実行・・・1.750 1.360 1.37 1.407 1.672 1.531 5×10^7で実行・・・1.859 1.797 1.812 1.800  10^8で実行・・・2.062 2.140 2.320 2.500 このような結果になりました。 これで正しいのでしょうか?もう少しずれの幅が小さいと決められそうなのですが…そもそもプログラムが間違ってるんでしょうか? でも、Nが大きくなるにつれて実行時間が増えてるのでこちらはまだいいんですが・・・問題は不成功探索の方です。 次に 不成功探索では↑のプログラムの 乱数vのところを変化させて v=rand() % N;という箇所を v=N;としました。 a[i]には0~N-1が入っているので、v=Nとすれば必ず不成功になるはずですよね? こうして実行してみると N= 10^6、5×10^6、10^7、5×10^7、10^8と値を変化させても 0.719 0.54 0.625 0.534 [micro sec] に近い値ばかり出てしまい、正しい値とは到底思えません。 不成功探索は成功探索より時間がかかるはずですよね?なのになぜこのようになってしまったのでしょうか? 後、Nと実行時間の関係とは、最終的に得られた結果を元にして「実行時間はlog2Nに比例している」と言えばいいんでしょうか? こういうものってどのように回答したらいいのかヒントだけでももらえると非常に有難いです。 長々とスイマセン。 少し気づいたことなど些細なことでも全然かまわないので、どうかよろしくお願いします。

  • ヒープソート 追加操作について

    配列を用いたヒープにデータを追加する。 この際、データの追加は配列の最後の要素に新たなデータを加え、ヒープ条件を満足するまで 親子間でデータの交換を行う。 要素数をnとしたら、この追加操作にかかる最悪時間計算量を求めよ。 この問題なのですが、ただ単にデータを一つ追加する際の最悪時間計算量だったら、 オーダlog n ですが、 追加する要素がnこだったら、n* log nになります。 この問題ではどちらがより適切なのでしょうか? どなたかご教授ください。

  • 区分求積法

    区分求積法からlim(n->∞)1/nΣ(k=0,n-1)1/{1+(k/n)}は∫(0->1)1/(1+x)dxでlog2 となるのは、分かりますが、 (1)lim(n->∞)(1/n)^2Σ(k=0,n-1)1/{1+(k/n)}は  単純にlog2/nとして、0にはならないと思います。  こんなことをしたら、区分求積法をわかっていないといわれてしまう  と思います。これを正しく解くにはどうしたら良いでしょうか。 (2)lim(n->∞)1/nΣ(k=0,n-1)1/{1+(k/n)*((k-1)/n)}も  単純に(k-1)/nの部分をk/nとはできないと、思いますが、  どうしたらよいでしょうか。 よろしく、お願いします。    

  • 区分求積法の問題

    (1)lim(n→∞) 1/(n+2)+1/(n+4)・・・・+1/3n (2)lim(n→∞) (√1+√2+・・・・√n)/n√n という問題です。 (1)log√3(2)2/3 と一応でたのですが、あまり自信がないので教えて下さい。 ------------------------------------------------- 区分求積法を使うときは、ほとんどの問題は1/nをくくりだす方針で行けばだいたいうまくいくでしょうか? よろしくお願いします。

  • 自然数を5進法で表すには??

    【問】 xは10桁の自然数で、その最高位の数は3である。このxを5進法で表すと何桁となるか。 ただし、log10(2)=0.3010 log10(3)=0.4771とする。 という問題なのですが、自分で考えてみて 3*10^9≦x≦4*10^9 log10(3)+9≦log10(x)≦log10(4)+9 9.4771≦log10(x)≦9.6020  ……(1) 5^(n-1)≦x≦5^n n-1≦log5(x)≦n    ・    ・    ・ というとこまで考えたのですが、この先どうすればよいかわかりません。 よろしくお願いします。

  • フィボナッチヒープです

    今、ダイクストラ法を用いて最短経路問題についてのプログラムを作成して完成したところです. このダイクストラ法をフィボナッチヒープを用いて高速化できると聞きました. ウィキペディァで見てもよくわかりません。。。。 どなたか、このフィボナッチヒープの仕組みなど解説しているサイトを教えて下さい. できたら、この場での解説もお願いします.

  • 多倍長演算における実行時間と計算量の差

    数学の論文において数値実験が必要だったため, 多倍長演算をC++で実行したところ,計算量とのギャップが生じました. その原因をコンピュータに詳しい方にアドバイスを頂きたいと思って質問させていただきました. 具体的には,n,m を同ビット として 以下の二つの演算を考えます. 演算(1) n * m 演算(2) n^2 % m (n^2は先に計算しておき,% (mod)の演算のみ) -------------------------------------------------- ■計算量評価 大雑把に(1)と(2)の計算量を比較すると (1) lg n * lg m = (lg n)^2 (2) (2*lg n - lg m) * lg m = (lg n)^2 となるので,(1) と (2)はほぼ同じ計算量となります. -------------------------------------------------- しかしながら,実際に計算をしてみると, n,m が 1000bit ほどまでは,ほぼ同じ計算時間なのですが, 2000bit, 4000bit ,..., と数を大きくしていくと,大きくしただけ (2) の速度が遅くなります. 具体的な実験結果は画像で添付いたします. 画像 (http://puu.sh/6iBQf.png) (1) と (2)の実行時間のギャップは何処から生じたものなのか,何かわかるかたがいらっしゃいましたら教えていただけたら嬉しいです. よろしくお願い致します. 予想:  コンピュータの知識があまりないですが,自分なりの予想では,(2)のn^2という数が大きすぎるため,演算においてメモリ間とのデータのやり取りで何かのオーバーヘッドが生じているのではないかと予想していますが,確証がもてません.

  • ちょっと難しい式変形

    (1-m)(log (2π)^1/2)+1/2((log n)-(log n_1+・・・・+log n_m)) =A(log n) になることを示せ (log nでくくれる事を示せばよい) ただし、 π:円周率 n_1+n_2+・・・+n_m=n わかる方いましたらよろしくお願いします。

  • 対数logについて

    対数logについて 基本情報処理技術者試験の勉強をしているのですが、 オーダ記法のところで、よく使われる表現として O(1) O(log n) O(n) O(n log n) O(n^2) ←計算時間小 計算時間大→ という五つの表現が紹介されているのですが、 このうち対数logを用いた物が理解できません。 例えば0(n)は、データ量nが2倍に増えれば計算時間も2倍になるということですよね。 しかしO(log n)だとどういう考え方になるのかわかりません。 対数についてもまだ理解できていないので、 対数のことから教えていただけないでしょうか。回答お待ちしてます。

  • Rubyで実行結果を時間差をつけて一行ずつ表示?

    10000を 10でわっていくプログラムです。これを普通に実行すると、一度に実行結果が、表示されますが、秒数を設定して、一行ずつ間隔をあけて、表示するには、どのようにプログラムを書けばよいでしょうか? よろしくお願いましす。  ソースコード n=10000 while n>=1 do break if n <= 1 if n%2 == 0 n = n / 10 end puts n end 実行結果 1000 100 10 0 ↑ この実行結果を  1000  1秒後に 100  1秒後に 10 1秒後に 0 のように 時間差をつけたいです。

    • ベストアンサー
    • Ruby

専門家に質問してみよう