駅間の最短距離探索プログラム作ってみた

はじめに

こんにちは。Yuinaです。

今日は東京の駅間の最短経路を見つけて、表示するプログラムを作成しました。

解説していきます。よろしくお願いいたします!

作成したコード

import heapq

def dikstra(graph,start_node,end_node):

distances = {node: float('inf') for node in graph}
distances[start_node] = 0
pre_distances = {node: None for node in graph}
priority_queue = [(0,start_node)]

while priority_queue:
current_time,current_node = heapq.heappop(priority_queue)

if current_node == end_node:
break

if current_time > distances[current_node]:
continue

for neighbor,travel_time in graph.get(current_node,{}).items():
new_time = current_time + travel_time

if new_time < distances[neighbor]:
distances[neighbor] = new_time
pre_distances[neighbor] = current_node
heapq.heappush(priority_queue,(new_time,neighbor))

return distances[end_node] ,pre_distances

def root_path(pre_distances,end_node):

path = []
current = end_node

while current is not None:
path.append(current)
current = pre_distances.get(current)

return path[::-1]


TOKYO_MAP = {
'池袋': {'新宿': 5, '目白': 2, '高田馬場': 7, '巣鴨': 5, '板橋': 4},
'目白': {'高田馬場': 2},
'高田馬場': {'新大久保': 2, '練馬': 15},
'新大久保': {'新宿': 2},
'新宿': {'渋谷': 7, '池袋': 5, '品川': 15},
'渋谷': {'新宿': 7, '恵比寿': 2, '品川': 11},
'恵比寿': {'渋谷': 2, '品川': 9},
'巣鴨': {'池袋': 5, '上野': 10, '水道橋': 8},
'板橋': {'池袋': 4, '練馬': 10},
'練馬': {'板橋': 10, '高田馬場': 15},
'上野': {'巣鴨': 10, '浅草': 5, '東京': 3},
'浅草': {'上野': 5},
'品川': {'新宿': 15, '渋谷': 11, '恵比寿': 9, '東京': 7},
'水道橋': {'巣鴨': 8, '東京': 5},
'東京': {'上野': 3, '品川': 7, '水道橋': 5}
}

start = '新宿'
end = '東京'

min_time,pre_distances = dikstra(TOKYO_MAP,start,end)
shortest_path = root_path(pre_distances,end)

print(f"--- {start} から {end} までの最短経路(最速時間)---")
print(f"最短時間: {min_time} 分")

if min_time != float('inf'):
print(f"最短経路:{' ->'.join(shortest_path)}")
else:
print("駅間の経路は見つかりませんでした。")

解説

メソッドの解説をしていきます。

dikstraメソッド

こちらは、ダイクストラ法で最短ルートを見つけ出すメソッドです。

  distances = {node: float('inf') for node in TOKYO_MAP}
distances[start] = 0
pre_distances = {node: None for node in TOKYO_MAP}
priority_queue = [(0, start)]

distancesにすべての場所への移動時間を初期化します。

distances = { ‘新宿’: float(‘inf’), ‘東京’: float(‘inf’), ‘渋谷’: float(‘inf’) }

という感じのイメージになります。

続いて、

distances[start] = 0で出発点を更新します。

distances = { ‘新宿’: 0, ‘東京’: float(‘inf’), ‘渋谷’: float(‘inf’) }

出発点を新宿にしたら、こんな感じでしょうかね。

pre_distances = {node: None for node in TOKYO_MAP}

は、これまでの経路を記録します。

こんなイメージです↓

distances[i]pre_distances意味 (矢印の向き)
'新橋''品川'新橋へ最短で着くルートは、品川から来るものだ。
'品川''東京'品川へ最短で着くルートは、東京から来るものだ。
'東京'None東京(出発地)の直前は存在しない。

続いて、

priority_queue = [(0,start_node)]

ですが、ダイクストラ法を始めるために最初のアイテムを準備し、キューに追加します。

表すと以下のようになります。

順序要素意味
1番目0移動時間(優先度の基準)
2番目start_nodeノード名(駅名)

続いて、最短ルートを探索します!!

  while priority_queue:
current_time,current_node = heapq.heappop(priority_queue)
 if current_node == end_node:
break

優先度キューに場所が残っている限り、調べ続けます。

移動時間が短い順から調べられるようにheapqを使います。

ここで、スキップ処理で遠回りなルートは無視します。

if current_time > distances[current_node]:
continue

続いて、今いる場所から行ける隣の場所をチェックします。

for neighbor,travel_time in TOKYO_MAP.get(current_node,{}).items():
new_time = current_time + travel_time

# current_time:出発地点から今いる場所までの合計最短時間
# traveL_time:駅(例:品川~新橋)の区間移動時間
# distances[neighbor]:これまで発見してきた最短経路

先ほど計算した、新しい合計時間(new_time)と隣の駅(neighbor)への最短時間を比較し、最短時間を更新します。

neighborの一つ手前の駅としてcurrent_nodeを記録します。

(新しい最短ルートは「今いる駅(current_node)を通ってきた」ものです)

そして、新しく見つかった、より短いルートを、ダイクストラ法の探索リストに組み込みます。

priority_queueは、最短時間を基準に自動で並び替えてくれる特別なヒープです。

(new_time,neighbor)は追加されるタプル(検索候補)です。

if new_time < distances[neighbor]:
distances[neighbor] = new_time
pre_distances[neighbor] = current_node
heapq.heappush(priority_queue,(new_time,neighbor))
return distances[end_node] ,predecessors

root_pathメソッド

ここでは、dikstraメソッドが見つけた情報を使って、

実際にどんなルートで移動したのか、内容を取得します。

  path = [] #ここに経路を入れる
current = end_node # 現在地を目的地に設定

while current is not None:
path.append(current) # 経路を追加
current = pre_distances.get(current) # 1つ前の駅を取得
retun path[::-1] # リストを逆さまにして返す

目的地から出発地に向かって、pre_distancesの情報を頼りに
1つずつ場所を遡っていきます。

pre_distancesは現在地の1つ前のが格納されています。※再度掲載しておきます。

キー (駅)値 (直前の駅)意味 (矢印の向き)
'新橋''品川'新橋へ最短で着くルートは、品川から来るものだ。
'品川''東京'品川へ最短で着くルートは、東京から来るものだ。
'東京'None東京(出発地)の直前は存在しない。

実行結果

ありがとうございました✨

タイトルとURLをコピーしました