地下鉄路線図のための高速経路探索アルゴリズム

地下鉄路線図のための高速経路探索アルゴリズム

1. 概要

過去2日間、Blog Parkで地下鉄マップの実装について話していました。その前に、私もクラスメイトのNeoRAGEx2002と一緒にAndroidの地下鉄マップアプリケーションを作成しました。そのため、地下鉄マップの経路検索アルゴリズムについては、ブログを書いて、私たちのソリューションを皆さんの参考として提供する必要があると思います。この論文で説明されているアルゴリズムの時間計算量は O(|E|log|E|) です。ここで、|E| はエッジの数です。

2. コンセプト

1) 点と辺

基本的な要素は、ポイント (地下鉄の駅) とエッジ (隣接する 2 つの駅間の有向線路) です。

たとえば、新荘駅を通過する 1 号線と 5 号線があり、新荘駅を含むエッジは 4 つあります。また、世紀大道駅を通過する路線は 4 つあり、世紀大道駅を含むエッジは 8 つあります。

2) 運用段階

エッジに基づいて、連続したエッジのセットである操作セグメントの概念もあります。

例えば、1号線は、新荘〜富錦路(出発間隔8分)、新荘〜上海駅(出発間隔6分)、上海南駅〜富錦路(出発間隔8分)、上海南駅〜上海駅(出発間隔6分)、富錦路〜新荘(出発間隔8分)、上海駅〜新荘(出発間隔6分)などの運行区間があります。

3) コスト

経路探索アルゴリズムの基礎は、時間、転送回数、通過したエッジ数などの非負のコストになります。ここでは、モデリング時間に焦点を当てます。

各エッジには移動時間コストがあり、これは地下鉄に乗ってエッジを通過するのに必要な時間を表します。

各運行区間には待機時間コストがあり、これは運行区間を通過する側バスに必要な待機時間です。これは通常、出発間隔(最大待機時間)または出発間隔の半分(待機時間の数学的期待値)と想定できます。

各ポイントには転送時間コスト マトリックスがあり、これは任意の 2 つのエッジ間の転送に必要な時間を表します。両者の関係には、直接接続、転送、接続なしの 3 つのタイプがあります。接続された転送の時間コストは 0、転送の時間コストは転送歩行時間 + 待機時間、切断された転送の時間コストは +∞ です。この行列は疎行列で表すことができ、切断された辺は表示されません。地下鉄は、ある路線に沿って引き返すルートを考える必要がないように設計されているため、乗り換えではなく片側と反対側を切断したものと見なすことができ、グラフの複雑さを軽減できます。

3. アルゴリズム

1) アイデア

従来の最短経路アルゴリズムには、次のようなものが数多くあります。

Dijkstra アルゴリズムですが、このアルゴリズムでは転送時間コストの問題を解決できません。

幅優先アルゴリズムでは、重み付きグラフの場合、最適なソリューションを取得できません。

制限付き深さ優先アルゴリズムでは結果を得ることができますが、パスが長い場合はアルゴリズムに時間がかかりすぎます。

山頂の雪が溶けて谷間を流れていく自然現象を思い浮かべることができます。各駅は谷間の地点であり、乗換駅は谷が複数の支線に分かれる交差点です。

出発点を山頂とすると、水は各エッジに沿って拡散します。1 つのエッジを通過するのにかかる時間は、エッジに乗る時間コストと同じです。1 つのエッジから隣接するエッジに移動するには、乗り換えを待つ時間コストが必要です。開始点まで水を注ぎ続けると、水は流れ続けます。水が終了点に到達したとき、水が通った経路は、必要な最短経路です。

このモデルの問題は、水が同時に複数の流れを流れる可能性があるが、アルゴリズムには順序がある必要があることです。すべての水の流れの前面の位置を表す水の流れの接線があると想定できます。任意のエッジ e について、その開始点が水の流れで覆われているが、終了点が水の流れで覆われていない場合、コストでソートされた接線エッジ リスト C (赤黒木またはバランス木によって実装される) に e を追加し、e -> 水の流れが通過した前のエッジを記録します。水の流れを続けると、まず C の最初の辺 e の終点が水の流れに覆われ、e は C から削除されます。パス検索の終了点に到達すると、最後のエッジから前のエッジ、さらに前のエッジの前のエッジへと遡って、パス検索の開始点に到達するまで、必要なパスを取得できます。

このアルゴリズムは、終点ではなく、パフォーマンスに顕著な影響を与えることなく、水の流れがマップ上のすべてのポイントをカバーするまで終了することもできます。

2) 例

図1に示すように:

図1 (a) 時間コスト 図1 (b) 検索順序

問題を単純化するために、ライン 2 (緑) とライン 9 (水色) は存在しないものとし、ライン 4 (濃い青) とライン 6 (紫) のみを考慮します。

図1(a)は、ライン4とライン6のエッジの時間コストを示しています。白は待ち時間、黄色は乗車時間を表しています。

各乗り換え駅での乗り換えにかかる徒歩時間は4分と想定しています。

図1(b)は探索順序を示しています。同じコストの場合、探索順序は不確定であり、接線エッジリストCの実装によって決定されます。

この例では、出発点は Century Avenue、到着点は上海小児医療センターです。

接線エッジリストCは次のように変化する。

  1. {1、2、3、5}
  2. {2、3、4、5}
  3. {3、4、5、6}
  4. {4、5、6、9}
  5. {5、6、7、9}
  6. {6、7、8、9}
  7. {7、8、9、10、..、..}
  8. {8、9、10、..、..、..}
  9. {9、10、..、..、..、..}
  10. {10、..、..、..、..、..}

6 が削除されると、10 (藍村路、塘橋) の 3 つのエッジと 9 の逆エッジが追加されることに注意してください。9 が削除されると、6 の逆エッジが追加されます。

9が消えたら、再び10を探します。この時の時間コストは13+4+8=25ですが、10はすでに上辺を記録しているので、Cは追加されません。

#p#

3) 実装

疑似コードは次のとおりです。

  1. 頂点を記録する//point  
  2. InEdges:List<Edge> //エントリエッジ 
  3. OutEdges:List<Edge> // アウトバウンドエッジ 
  4. Connection:Map<Tuple<Edge, Edge>, EdgeConnection> //接続されていない場合は、転送歩行時間コストを含むエッジ接続マトリックスは存在しません。  
  5.  
  6. レコードエッジ//edge  
  7. Start:Vertex //開始点 
  8. End:Vertex //終点 
  9. コスト: Int //乗車時間コスト 
  10. Ranges:List<Range> //操作セグメント 
  11.  
  12. レコード範囲//操作セグメント 
  13. エッジ:List<Edge> //エッジ 
  14. コスト: Int // 待機時間 
  15.  
  16. タグ付きユニオン EdgeConnection
  17. 接続:ユニット//直接接続 
  18. Transferable:Int //転送、歩行時間コスト 
  19.  
  20. CalculateRoute(開始:頂点、終了:頂点):List<Edge>
  21.     開始 == 終了の場合
  22.         戻る  new List<Edge>() //開始点と終了点が一致する 
  23.  
  24. let Previous <- new Map<Edge, Edge>() // エッジから前のエッジへのマッピング 
  25. let cmp <- (Comparer<Edge>)(...) // パスコスト比較関数。以下に示す 
  26. let CutEdges <- new RedBlackTree<Edge>(cmp) //水流接線エッジリスト 
  27.  
  28. Start.OutEdges の o を foreach する
  29. カットエッジ <- カットエッジ + o
  30. 前 <- 前 + (o, null)
  31.  
  32. let e <- (Edge)(null) // エッジの終了 
  33.  
  34.      CutEdges.Count > 0 の場合
  35. i <- CutEdges.First とします
  36. カットエッジ <- カットエッジ - i
  37.  
  38. s <- i.End とします
  39.          s == 終了の場合
  40. え <- 私
  41.             壊す 
  42.  
  43. s.OutEdges 内の o を foreach する
  44.              !s.Connection.ContainsKey((i, o))の場合
  45.                 続く 
  46.  
  47.              Previous.ContainsKey(o)の場合
  48.                 続く 
  49.  
  50. 前 <- 前 + (o, i)
  51. カットエッジ <- カットエッジ + o
  52.  
  53.      e == nullの場合
  54.          nullを返す// パスなし 
  55.  
  56. l <-新しいリスト<Edge>() を記述します。
  57.      e != null の場合
  58. l <- l + e
  59. e <- 前へ(e)
  60.  
  61.      l.Reverse()を返す

以下は、経路探索の基準が時間である場合の比較関数です。

  1. Time <-新しいMap<Edge, Int>() を作成します。
  2. Range <-新しいMap<Edge, Range>()を作成します。
  3. GetBestRange <- l:List<Range> => l.OrderBy(r => r.Cost).First を実行します。
  4. GetTime <- を取得します。
  5. e =>
  6.          e == nullの場合
  7.              0を返す
  8.          Time.ContainsKey(e)の場合
  9.              Time(e)を返す
  10. p <- 前へ(e)
  11. v <- GetTime(p) とします。
  12.          p != null の場合
  13. c <- e.Start.Connection((p, e)) とします。
  14.             もしc
  15. | 接続済み ->
  16. rgOld <- Range(p) とします。
  17. rg <- GetBestRange(p.Ranges.Intersect(e.Ranges)) とします。
  18. 範囲 <- 範囲 + (e, rg)
  19.                  rgOld != rgの場合
  20. v <- v - rgOld.Cost + rg.Cost
  21. | 譲渡可能 t ->
  22. rg <- GetBestRange(e.Ranges) を実行します。
  23. 範囲 <- 範囲 + (e, rg)
  24. v <- v + rg.Cost + t
  25.         それ以外 
  26. rg <- GetBestRange(e.Ranges) を実行します。
  27. 範囲 <- 範囲 + (e, rg)
  28. v <- v + rg.コスト
  29. v <- v + e.コスト
  30. 時間 <- 時間 + (e, v)
  31.         リターンv
  32. cmp <- をします
  33. (l:エッジ、r:エッジ) =>
  34.          GetTime(l) - GetTime(r)を返す

以下は、経路探索基準が乗り換え回数である場合の比較関数である。

  1. TransferCount <-新しいMap<Edge, Int>()を作成します。
  2. GetTransferCount を実行します <-
  3. e =>
  4.          e == nullの場合
  5.              0を返す
  6.          TransferCount.ContainsKey(e)の場合
  7.              TransferCount(e)を返す
  8. p <- 前へ(e)
  9. v <- GetTransferCount(p) を実行します。
  10.          p != null の場合
  11. c <- e.Start.Connection((p, e)) とします。
  12.             もしc
  13. | 接続済み ->
  14. ()
  15. | 譲渡可能 _ ->
  16. 1 + = 1
  17. 転送回数 <- 転送回数 + (e, v)
  18.         リターンv
  19. cmp <- をします
  20. (l:エッジ、r:エッジ) =>
  21.          GetTransferCount(l) - GetTransferCount(r)を返す

以下は、パス検索の基準が通過したエッジの数である場合の比較関数です。

  1. StopCount <-新しいMap<Edge, Int>()を追加します。
  2. GetStopCount を実行します <-
  3. e =>
  4.          e == nullの場合
  5.              0を返す
  6.          StopCount.ContainsKey(e)の場合
  7.              StopCount(e)を返す
  8. p <- 前へ(e)
  9. v <- GetStopCount(p) + 1 とします
  10. ストップカウント <- ストップカウント + (e, v)
  11.         リターンv
  12. cmp <- をします
  13. (l:エッジ、r:エッジ) =>
  14.          GetStopCount(l) - GetStopCount(r)を返す

4. アルゴリズムの複雑さ

ポイントの入側エッジと出側エッジが少なく、各エッジをカバーする操作セグメントが少ないと仮定し、GetTime の再帰部分は実行時に常に Time 変数にキャッシュされることに注目すると、時間比較関数の複雑さは O(1) であることがわかります。

CutEdges の赤黒木の挿入と削除の複雑さは O(log|E|) です。

すべてのエッジは CutEdges に最大 1 回だけ出入りするため、アルゴリズム全体の複雑さは O(|E|log|E|) です。

5. 結果

この論文で説明したアルゴリズムは、O(|E|log|E|) 時間でグローバル最適パスを迅速に取得できます。

1GHz シングル CPU の携帯電話で測定された上海地下鉄 (11 路線、214 駅) の任意の 2 つの駅間の経路探索時間は 200 ミリ秒未満です。

最後に、私たちのアプリケーションを紹介したいと思います。

ベクター地下鉄(上海版)

2 本指の無限ズーム、動的ルーティング効果、ローカル マップ表示をサポートします。私たちは小さなチームですが、最高の地下鉄地図だけを作っています。ご質問やご提案がございましたら、メッセージをお寄せください。

オリジナルリンク: http://www.cnblogs.com/Rex/archive/2012/08/12/2634401.html

<<:  HTML5アウトラインアルゴリズムが構造に与える影響

>>:  データマイニングのためのK平均法アルゴリズムのグラフィカルな説明

ブログ    
ブログ    
ブログ    

推薦する

UiPath Carnivalは職場の自動化におけるイノベーションを探るために近日開催されます

ロボティック・プロセス・オートメーション(RPA)エンタープライズソフトウェア企業のUiPathは最...

そうだ!機械学習を使用してビリビリの株価動向を予測する

[[419019]]この記事では、主にPythonを使用してビリビリの株価を分析する方法について説明...

PageRankアルゴリズムとPR値の転送の詳細な分析

PageRank アルゴリズムは、Google のランキング アルゴリズム (ランキング式) の一部...

...

人工知能はよりクールで実用的

2021年は間違いなく人工知能産業の発展にとって重要な年となるでしょう。わが国のスマートシティ建設の...

滴滴出行とスタンフォード人工知能研究所が協力

滴滴出行は5月5日、スタンフォード人工知能研究所との提携を発表した。両者は人工知能のホットな話題につ...

...

人工知能はデマですか?人工知能が日常生活にもたらす変化を感じられますか?

しかし、メディアで大いに宣伝された後、人々は AlphaGo が Deep Blue と同じレベルに...

GANは音声を使って画像を生成できるようになった

[[432735]]この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI...

ダボにおけるタイムホイールアルゴリズムの応用

[[346568]] 1 スケジュールされたタスクNetty、Quartz、Kafka、Linux ...

2020年Qizhi開発者会議が北京で盛大に開幕、第一弾の1000万インセンティブボーナスが発表された

2020年12月2日午前9時、知恵とリソースを集めることを目的とした2日間のOpenI/O 2020...

...

...

ガートナー:2026年までに企業の80%が生成型AIを導入する見込み、これは現在の16倍にあたる

アナリスト会社ガートナーは10月13日、2026年までに企業の80%以上が生成型AIアプリケーション...

デンマークのAIモデルは保険会社よりも正確に死亡率を予測し、乱用を懸念

12月19日、デンマーク工科大学のスニ・レーマン・ヨルゲンセン氏と彼のチームは、保険業界で使用されて...