Python + scipy を使って最短経路もついでに取得する方法
こんばんは。kawap23です。
前回の記事で、最短経路知りたいなぁって言っているとTwitterで教えてくださった方がいるので、記事にして残しておきたいと思います。(maspyさん、ありがとうございます。)
あくまで、使うためですので、正確性や厳密性は保証しません。
scipyを使って最短距離問題を解く方法にいついては、前回の記事を見てください。
最短経路を求める方法
いきなり結論ですが、dijsktra(グラフ、有向 or 無向、始点、経路が必要 or 不要)と引数に一つ足すだけで、最短距離と同時に返ってきます。(numpy.ndarrayが2つ返ってくる)
- 経路:return_predecessors = True で経路が返ってくる (デフォルトはFalse)
- 返ってきたarrayには直前の頂点情報が入っているため、始点までたどることで最短経路をたどることができる。
- 始点には -9999 が入っている
- 始点と連結していない点にも -9999が入る
とこんな感じです。ちなみに始点に2つ以上リスト等の形で指定しても問題なく返ってきます。
使用例は以下の感じです。入力例は蟻本のものに非連結成分を追加したものです。
import numpy as np from scipy.sparse import csr_matrix from scipy.sparse.csgraph import dijkstra N, M = map(int, input().split()) edge = np.array([input().split() for _ in range(M)], dtype = np.int64).T graph = csr_matrix((edge[2], (edge[:2] - 1)), (N, N)) distance_mat, processors = dijkstra(graph, directed = False, indices = [0, 1], return_predecessors = True) print ('点0からの最短距離') print (distance_mat) print ('各点の直前の点') print (processors) # 入力例 # 9 11 # 1 2 2 # 1 3 5 # 2 3 4 # 2 4 6 # 2 5 10 # 3 4 2 # 4 6 1 # 5 6 3 # 5 7 5 # 6 7 9 # 8 9 4 # 出力例 # 点0からの最短距離 # [[ 0. 2. 5. 7. 11. 8. 16. inf inf] # [ 2. 0. 4. 6. 10. 7. 15. inf inf]] # 各点の直前の点 # [[-9999 0 0 2 5 3 4 -9999 -9999] # [ 1 -9999 1 1 1 3 4 -9999 -9999]]
実行時間について
詳しい実行コードは載せませんが、N = 10^6, M = 10^7 で、10点に関して最短経路ありと最短経路なしで計算したところ、30.32秒と30.77秒とほとんど差がない結果でした。計算コストは最短距離を出す計算をするところがメインであり、最短経路はついでなのでまぁそうですねって感じです。
結論
最短経路を求めたい時は、引数にreturn_predecessors = True を加える。
返り値は、numpy.ndarrayが最短距離のarrayとは別に増える
計算コストは(多分)無視しても構わないくらい少ない(一点の情報の復元はO(N))