【事例演習2】 「最短経路探索 (1)」 その1 解 説 “最短ルート探索のアルゴリズム” 2001年11月更新 【 最短経路探索アルゴリズムはどこに役立つか 】 ①「カーナビゲーション・システム」や「駅すぱーと」等の 最短距離で到達できる経路探索に用いられるアルゴリズム である(情報処理技術者試験にもよく出題される代表的な アルゴリズム). ② 距離の変わりに,いろいろな計数を当てはめると, 通信ネットワーク経路の選択や,最適化問題にも用いること ができる. 最短ルート探索の考え方(1): トークンが等速度で移動する 始点ノードから出発して,最初に終点ノードに到着したトークンの通った経路が, 最短経路である.実に単純明快!! リンクの距離 Miss トークン1 4.5 2 1.4 1.1 2.6 始点ノード 1 同時にトークンが 等速度でスタートする 6 1.5 3 1.6 2.8 5 5.7 3.3 1.0 【考え方1】 リンク(道路) ノード(交差点) 4 【考え方2】 トークンがノードに到着すると(計算上は 一気に次のノードへ進ませる),そこから さらにトークンが等速度でスタートする 2.4 6.0 7 終点ノード 8 最短ルート探索の考え方(2):トークンの動きを記録する 知りたいのは,最短でノードに到着したトークンが経由してきた“距離”と“1つ前の 経由ノード”である.これはすべてのノードで同じ.そこで,各ノードは最短で到達 したトークンの“経由距離”と“1つ前の経由ノード”を記録する(トークンが到達す るまでは経由距離の初期値は ∞) ∞ 1.4 通過したトークンの記録係 4.5 2 1.1 3 2.6 1 始点ノード ∞ - 6 1.5 1.6 5 3.3 1.0 4 ∞ - ∞ 2.8 5.7 ∞ - 2.4 6.0 7 ∞ - 経由距離 1つ前の経由 ノード ∞ 8 終点ノード 最短ルート探索の考え方(3):トークンの動かし方 ● トークンの移動で考えるべき点は2つだけ!! (1) トークンがノードに到着したとき,何をするか (2) 次にどのノードのトークンが出発すべきか ∞ - 4.5 2 1.1 3 1 1.6 5 3.3 1.0 - 1.5 - 2.6 始点ノード 6 ∞ 1.4 ∞ 2.8 5.7 ∞ ∞ - 6.0 8 2.4 4 ∞ 7 ∞ - 終点ノード トークンがノードに到着したとき,何をするか 1 ① 後から到着したトークンの“経由距離”の方が短ければ,ノードの“経由距離”と “1つ前の経由ノード”を,そのトークンのものに置き換える (注) 計算上,トークンを距離に関係なく一気に進ませるため,後から到着したトークンの 方が経由距離が短い場合が生じる) ② 後から到着したトークンの経由距離の方が長ければ,そのトークンは出発 させない(出発しても最短でないことは明らか) 1.4∞ 11.4 4.5 2 1.1 2.6 1 始点ノード 2.5 ∞ 2.6 2 3 1 6 1.5 1.6 5 3.3 1.0 4 1.0∞ 1- ∞ - 6.0 2.8 5.7 ∞ - 2.4 7 ∞ - 経由距離 1つ前の経由ノード ∞ 8 終点ノード 次にどのノードのトークンを出発させるべきか 2 “経由距離”の一番短いノードのトークンを出発させる. なぜなら,経由距離の一番短いノードからトークンが出発するとき,本来ならまだ ノードに到着してない(トークンはすべて等速で動いていることを思い出す.計算 上,トークンを一気に進ませるため,次のノードに到着したかのように見えるだけ). 1.4 ∞ 1 - 4.5 2 1.4 1.1 2.6 1 始点ノード 6 1.5 1.6 3.3 1.0 トークンが出発し終わ ったら戻って来ないよう “出発し終わった”ことを 示すフラッグを立てる. 3 2.6 ∞ 1- 4 1.0 ∞ 1- ∞ - トークンが到着している ノードの経由距離が最小 6.0 5 2.8 5.7 ∞ - 2.4 7 ∞ - 経由距離 1つ前の経由ノード ∞ 8 終点ノード 最短ルート探索アルゴリズムの動作例 【手順1‘】 【手順2 【手順4】 【手順3】】 次にトークンが出発可能なノードは,経過距離が一番短いもの トークンの経由距離がノードに書き込まれている距離より トークンを出発させる.以下同様な手順を,動きうるすべてのトークンが トークンが戻って来ないよう,出発ノードにフラッグを立てる 【手順1 】 トークンをつながっているすべてのノードへ進ませる (なぜなら,他のトークンは実際の時間より早く次のノードに進んでいるから) 短ければ,“1つ前の経由ノード”をトークンの出発ノードに変更する 終点ノードに到着するまで繰り返す 1.4∞ 11.4 4.5 2 1.1 3 2.6 1 始点ノード 6 6 2.5 2.6 ∞ 21 - 1.5 1.6 5 5 3.3 1.0 4 1.0 ∞ 1- 5.6 5.9 ∞ 52 2.8 4.1 4.3 ∞ 3 4- 5.7 1つ前の経由ノード 8.49.8 ∞ 65 - 8 2.4 6.0 経由距離 7 7.0 ∞ 4- 終点ノード ※ 最短ルート探索アルゴリズムをプログラムに 置き換えるときの考え方 ① トークンが在るノードの番号を格納する変数Pを用意する if(P==8) ∞ - 2 P=2; // ノード8にトークンが在るかを調べる // トークンが在るノード2に着目することを意味する ② トークンの移動先ノードを示す変数qを用意する 1.4 q=2; 1 1.0 // ノード2にトークンを移動させる ③ノード1に接続するすべてのノードへの距離はノード1がもっている. 他のノードも同じ. 4 最短経路探索のアルゴリズム 経由経路表(route)の初期値として0,最短経由距離表(totalDist) の初期値として ∞をそれぞれ設定する p に 始点ノード番号をセットする pが終点ノード番号に一致するまで繰返す // トークンをpに置く // トークンがpに到着するまで繰り返す トークンがいるノードpに接続するすべてのノードに繰返す // 接続先にトークンを移動 トークンの到着 qを接続先のノードの番号とする; //トークンの1つの移動先をqとする ノードqまでの距離をdist_q とする /* (1)ノードqに到着したトークンの経過距離比較 */ ノードqに出発済みのフラッグが立っていなくて,かつ Yes ノードqの経由距離 > ノードpまでの経由距離 + dist_q /* ノードpを経由したルートの方が,経由距離合計が短いので ノードp経由に変更する */ ノードqの“経由距離”をノードpまでの“経由距離 ”+ dist_qにする; ノードqの“1つ前の経由ノード”をpにする; ノードqのフラッグを下げる Yes トークンが出発済みであることを示すために,ノードpのフラッグを上げる トークンの 出発 /* (2) 次にトークンを出発させるノードの決定 */ ネットーク全体からフラッグが立っていなくて,かつ経由距離の最も短い ノードを選び出し,次にトークンを進めるべきノードをpとする No ネットワークをどのようなデータ構造で表現するか 通常は2次元行列 (ノード数nの二乗) ※無駄な記憶領域と計算スピードを上げるためのデータ構造 《 存在するノードのデータのみを格納する 》 ノード番号 2 1 ノード表 (Node) 3 1 8 ・・・ 3 4 22 3 リンク表において,ノード1の接続先デ ータが格納されている要素の開始位置 (link_position) ノード1に接続するノードの個数 (link_size) 1 リンク表 (Link) 2 接続先ノード番号 (connect_node) 2 1.4 3 3 2.6 4 1.0 1 接続先ノードまでの距離 (dist) 4 1.4 3 5 6 1.1 6 4.5 22 5 5.7 23 6 24 2.8 7 2.4 大規模なネットワークでもっと計算を早くするに は! 次に出発すべきノードを見出す手順に,無駄が多すぎる! 1.4 1 4.5 2 1.4 3 2.6 1 1.0 1 1.0 6 2.6 1 1.1 4 1.5 1.6 5 3.3 3 6.0 4 ノード4からすべて出発した後,次に調べるべきノード 2 3 5 7 2.8 5.7 ∞ - わかりきっている ので調べないよう にする ∞ 8 2.4 ノード1からすべて出発した後,次に調べるべきノード 2 ∞ - 7 ∞ - 待ち列(キュー)を利用すると 次に出発すべきノードが早く見出せる ①ノード1から出発したリンク先のノードを調べるべきノードとして追加する 2 3 キューに追加 4 最後の要素位置 ②ノード4が次に出発すべきノードとわかったので取り出す キューから 取り出す 2 3 ③ノード4から出発したリンク先のノードを調べるべきノードとして追加する 2 3 5 7 キューに追加 「最短経路探索(1)」 その2 解 説 “3次元オブジェクトの直接操作” 描画した3次元オブジェクトには必ず オブジェクト番号が付く ObjID_Runner = PiasSphere( gp , x , 0 , z , 20); // 球 オブジェクト番号を格納するint型変数: ObjID_Runner ●オブジェクトに名前(例えば“Runner”)を付けてから,3次元オブジェクトを 描き,その名前でオブジェクト番号を調べる方法もある PiasDeclareName(gp,“Runner”); // これから作るオブジェクトの名前 PiasSphere( gp , x , 0 , z , 20); // 球 ObjID_Runner = PiasObjectNumber(gp,"Runner"); 画面上の座標x1,y1にある オブジェクトの番号を調べるには 画面座標 X座標 Y座標 (x1,y1) ObjNo = PiasTarget(gp, (XYZV) x1 , (XYZV) y1 ); この関数を用いる! どうやって調べているのか (案1)この領域の中に入っているかで調べる ではこのような(オブジェクト1に穴があり, そこからオブジェクト2が見える)ときは,どうするか 指定した点 オブジェクト1 オブジェクト2 ここの点はオブジェクト2のはず この領域の中にあるを調べただけではダメ PiasTargetが用いている方法 ダブルバッファ法を利用する. ダブルバッファ法とは? 【表示中の画面】 2画面を切り換え, スムーズな動きを作る 【バッファ画面】 ちょっと動かした画面を 表示中に再描画 (1) PiasTargetが呼び出されたとき,もう一方の描画バッファの方で, オブジェクト番号を色番号と解釈してオブジェクトを描きなおす. (2) 指定した画面座標のピクセルがどのような色(16ビット)であるかを 調べるOpenGLの関数を用いてオブジェクト番号を得る. 【参考】 プログラミングの方法 キーと押下しながら,マウスの左ボタンを押したとき その位置にあるオブジェクトの番号を知るには? イベントハンドラ関数 PiasTK_LBUTTONDOWN( HWND hWnd , LPARAM lParam ) { /*--------- マウス左ボタンが押下されたとき ---------*/ lParamにマウスの画面座標位置が セットされている if (指定したキーが押下の状態であるか) { //---- 指定したキーが押下されている状態のとき ---- int objID_Node; char str[20]; lParamの下半分はX座標 lParamの上半分はY座標 ObjID_Node = PiasTarget(gp, (XYZV) LOWORD( lParam ) , (XYZV) HIWORD( lParam ) sprintf(str, “始点ノード %d” , ObjID_Node); MessageBox (hWnd, str, “ノードの確認”, MB_OK); )+ 1 ; 押下されたキーの状態を記憶するには? イベントハンドラ関数 PiasTK_KEYDOWN( HWND hWnd , WPARAM wParam ) { Wparamには押下されたキー番号がセットされている switch (wParam) { case VK_CONTROL: CntlSts = SKEYDOWN ; break; case VK_SHIFT : ShftSts = SKEYDOWN ; break; case VK_TAB TabSts = SKEYDOWN ; break; default: break; } } :
© Copyright 2024 ExpyDoc