古典的な最短経路アルゴリズムの1つダイクストラ
序文
今日は、グラフ検索の最短経路について話します。ここでの最短経路には、あるノードから別のノードまで考慮する必要のある距離または時間の消費、つまり、に到達するのに必要な時間(または他の形式の消費)が含まれます。開始点からの終点が最小です。、前述の迷路検索カテゴリの最短パスとは異なり、パス上のノードが少なくなっています(つまり、ノード間の距離は1と想定されています)
。Ps:元の記事ブロガーのWeChatツイートで、そのうちのいくつかは連絡先になり、現在CSDNに移動しています〜
1.アルゴリズム原理の説明
実装言語:Python
上記の問題について、次の図を見てみましょう。
現時点では、グラフ上のノードをトラバースする必要があるようです。従来のBFS検索で得られた最短パスは、必ずしも最も時間がかかるとは限りません。
ここでは、古典的な単一ソースの最短経路検索アルゴリズムであるダイクストラのアルゴリズムを紹介します。単一ソースとは、指定された開始点から他のすべてのノードまでの最短距離を取得できることを意味します。
基本的に、これはループ最適化アルゴリズムです。各ループで、次の手順を実行します。
1.開始点から既知の距離が最も短いノード、つまり最も速く到達できるポイントを見つけます。2。このポイントに接続されている他のノードを介して、周囲のノードの距離コスト、つまり、これらの周囲のノードへの開始点
上記の図を例として、図を使用して簡単なアルゴリズムフローを示しましょう。
まず、最初にわかっている最短のノードは開始点自体です。最初に、すべての点を無限大に設定してから、既知のエッジを使用してこれらの値を更新する必要がありますが、ここで示したグラフには、与えられたエッジの重み。開始点Mから開始して、A、B、Nが検出され、重みが更新されます。
2番目のサイクルの開始時に、開始点に最も近い点をBとして見つけた後、Bに接続されている他のノードの検索を開始します。これらのノードは前のサイクルでマークされた最も近い点にはなり得ないことに注意してください。前のサイクルはこのラウンドのものです。開始点までの距離を更新することは意味がありません。すでに最も近いです。
条件を満たすAとNがあり、始点までの距離を更新していることがわかります。MB + BA <MAであることがわかります。つまり、開始点から点Aに到達する方法が近いため、MAの値が更新され、MNの値も更新されます。
3番目のサイクルでは、開始点に最も近い点はAです。Aに接続され、マークされていないノードを見つけます。Nしかないので、MA + AN = 10> MN = 8なので、距離の値はMNは更新されません。
次のサイクルは終点に到達した後に終了できるため、始点から終点までを含む、始点から他のすべてのノードまでの最短距離を取得できます。
2、コードの実装
def Dijkstra(directed=False):
"""
Functions: Implementation of Dijkstra using Python
Args:
directed: whether the graph is directed or not
return:
None
"""
# init distance
limit = 10000
# number of nodes, number of links, start_index, end_index
N, K, s, e = list(map(int, input().split()))
# graph mat
ad_mat = [[0 for i in range(N)] for j in range(N)]
# distance to start_node
Dis = [limit for i in range(N)]
# tags array
vis = [0 for i in range(N)]
# use links to fresh graph mat
for i in range(K):
u, v, w = list(map(int, input().split()))
ad_mat[u][v] = w
if directed == False:
ad_mat[v][u] = w
# init distance of start_node
Dis[s] = 0
# core logic
for i in range(N):
# select nodes with shortest distance to start from unchecked nodes
min_ind = Dis.index(min([Dis[k] for k in range(len(Dis)) if vis[k] == 0]))
# sign the node
vis[min_ind] = 1
for j in range(N):
# fresh distance from start_node to adjacent nodes
# unchecked, exist links, shorter then original dis
if vis[j] == 0 \
and ad_mat[min_ind][j] != 0 \
and Dis[j] > (Dis[min_ind]+ad_mat[min_ind][j]):
Dis[j] = Dis[min_ind]+ad_mat[min_ind][j]
print('Shortest distance from s to e: {}'.format(Dis[e]))
return
コードの詳細の説明
min_ind = Dis.index(min([Dis[k] for k in range(len(Dis)) if vis[k] == 0]))
この文は、前のループで最も近い点としてマークされていないノードから、開始点に最も近いノードのインデックスを見つけることです。
if vis[j] == 0 and ad_mat[min_ind][j] != 0 \
and Dis[j] > (Dis[min_ind]+ad_mat[min_ind][j]):
Dis[j] = Dis[min_ind]+ad_mat[min_ind][j]
これは、距離を更新するコアステップです(緩和プロセスとも呼ばれます):このサイクルの最も近いポイントAを見つけた後、隣接するノードからマークされていないものを見つけます(マークされている場合は、最も近いポイントであり、更新されていることを意味します)周囲のノード)、Aとのパスリンクがあり、開始点とAおよびAの間のノードとノードまでの距離は、現在の開始点からノードまでの距離よりも短く、始点から終点までが更新されます。
時間の複雑さ
コードの導入により、特定の実装の時間計算量はO(n ^ 2)であることがわかります。
サンプルテスト
テスト例を実行してみましょう:
>>> Dijkstra(directed=False)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 16
Time used: 0.01053s
>>> Dijkstra(directed=True)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 32
Time used: 0.01255s
上記のダイクストラアルゴリズムは、有向グラフと無向グラフの両方に適用できます。次に、次の2つの場合に最短経路を描画します。
3.BFSがダイクストラを実現
実現アイデア
次に、優先キューと組み合わせたBFSのアイデアを使用して、ダイクストラアルゴリズムを実装します。上記の説明から、各サイクルは、マークされていない開始点に最も近い点を見つけることであることがわかります。このプロセスBFSで最短パスを見つけるために抽象化することができます。
ただし、キューに追加されたノードの順序が狂っているため、優先度キューを使用して並べ替え操作を行い、比較できるオブジェクトを定義する必要があります。
このように、BFSを介して開始点に最も近い点に到達し、緩和操作を実行するたびに、比較プロセスが大幅に削減されます。全体として、各エッジは1回だけチェックされます。
まず、同等のキューオブジェクトを定義する必要があります。
# comparable object definition
class compare_obj:
def __init__(self, s, dis):
self.s = s # node_index
self.dis = dis # distance from start node to this
def __lt__(self, other):
return self.dis < other.dis
一部の配列と一時変数の設定は以前と同じであり、G配列が追加されて、各ノードの隣接ノードの数が記録されます。
コード
# 使用BFS结合优先队列实现Dijkstra
def BFS_Dijkstra(directed=False):
"""
Functions: Implementation of Dijkstra using BFS and PriorityQueue
Args:
directed: whether the graph is directed or not
return:
None
"""
import time
from queue import PriorityQueue
# comparable object definition
class compare_obj:
def __init__(self, s, dis):
self.s = s
self.dis = dis
def __lt__(self, other):
return self.dis < other.dis
# init distance
limit = 10000
# number of nodes, number of links, start_index, end_index
N, K, s, e = list(map(int, input().split()))
start = time.time()
# graph mat
ad_mat = [[0 for i in range(N)] for j in range(N)]
# distance to start_node
Dis = [limit for i in range(N)]
# tags array
vis = [0 for i in range(N)]
# number of adjacent nodes of one node
G = [[] for i in range(N)]
# use links to fresh graph mat
for i in range(K):
u, v, w = list(map(int, input().split()))
ad_mat[u][v] = w
G[u].append(v)
if directed == False:
ad_mat[v][u] = w
G[v].append(u)
# init distance of start_node
Dis[s] = 0
# BFS with Priority Queue
Q = PriorityQueue()
Q.put(compare_obj(s, Dis[s]))
while Q.qsize() != 0:
node = Q.get_nowait()
s, dis = node.s, node.dis
# if check, continue
if vis[s]:
continue
# sign the node
vis[s] = True
# fresh distance
for i in range(len(G[s])):
ad_node = G[s][i]
if not vis[ad_node] and Dis[ad_node] > (ad_mat[s][ad_node]+Dis[s]):
Dis[ad_node] = ad_mat[s][ad_node]+Dis[s]
Q.put_nowait(compare_obj(ad_node, Dis[ad_node]))
print('Shortest distance from s to e: {}'.format(Dis[e]))
print('Time used: {:.5f}s'.format(time.time()-start))
return
テストサンプル
テスト結果を見てみましょう。
>>> BFS_Dijkstra(True)
7 9 0 6
0 1 9
1 2 12
2 3 6
1 3 5
3 4 14
4 5 3
3 5 8
5 6 10
6 1 7
Shortest distance from s to e: 32
Time used: 0.00573s
BFSによって実装されたダイクストラアルゴリズムの実行時間は、指定された例のforループ実装の2倍速いことがわかります。
4、要約:
ダイクストラのアルゴリズムに加えて、他の古典的な最短経路検索アルゴリズムがあります。後で穴を埋めていきます。
異なるアルゴリズム間の緩和の考え方は基本的に同じです。つまり、より短いパスを見つけて距離の値を更新します。ただし、検索戦略と解決に適した問題の種類はさまざまです。