1.Dijikstraアルゴリズム理論
単一のソースノードから他のノードへの加重グラフでは、すべてのパスの中から最短パスを見つける方法は、ユニットノードの最短パスの問題です。ルーティングアルゴリズムでは、これと同様に、モデルを抽象化できます。ダイクストラのアルゴリズムは、1959年にオランダのコンピューター科学者ダイクストラによって提案されました。これは、ある頂点から他の頂点への最短経路アルゴリズムであり、有向グラフの最短経路問題を解決します。ダイクストラのアルゴリズムの主な特徴は、始点が中心であり、外層が終点に到達するまで拡張されることです。
Ø基本的な考え方
グラフGでダイクストラを通る最短経路を計算するときは、開始点sを指定する必要があります(つまり、頂点sから開始します)。さらに、SとUの2つのセットが導入されています。Sの機能は、最短経路の頂点(および対応する最短経路の長さ)を記録することです。一方、Uは、最短経路の頂点(および頂点から開始点sまでの距離)を記録します。最初は、Sには開始点sしかありません。Uはs以外の頂点であり、Uの頂点のパスは「開始点sから頂点までのパス」です。次に、Uからの最短パスを持つ頂点を見つけてSに追加します。次に、Uの頂点と、その頂点に対応するパスを更新します。次に、Uからの最短パスを持つ頂点を見つけてSに追加します。次に、Uの頂点と、その頂点に対応するパスを更新します。…すべての頂点がトラバースされるまで、この操作を繰り返します。
Ø操作手順
\ 1.最初は、Sには開始点sのみが含まれ、Uにはs以外の他の頂点が含まれ、Uの頂点の距離は「開始点sから頂点までの距離」です[たとえば、 Uの頂点vは(s、v)であり、sとvは隣接しておらず、vの距離は∞]です。
\ 2. Uから「最短距離の頂点k」を選択し、頂点kをSに追加します。同時に、頂点kをUから削除します。
\ 3.Uの各頂点から開始点sまでの距離を更新します。Uの頂点の距離が更新される理由は、kが前のステップで決定された最短経路の頂点であるため、kを使用して他の頂点の距離を更新できます。たとえば、(s 、v)は、(s、k)+(k、v)距離よりも大きい場合があります。
\ 4.すべての頂点がトラバースされるまで、手順(2)と(3)を繰り返します。
Øイラスト
上の図G4は、ダイクストラのアルゴリズムを示す例として取り上げられています(開始点として4番目の頂点Dを取ります)。次のノードBでは、23は13になります。
初期状態:Sは最短経路が計算された頂点の集合であり、Uは最短経路が計算されていない頂点の集合です。
ステップ1:頂点DをSに追加します。
このとき、S = {D(0)}、U = {A(∞)、B(∞)、C(3)、E(4)、F(∞)、G(∞)}。注:C(3)は、Cから開始点Dまでの距離が3であることを意味します。
ステップ2:頂点CをSに追加します。
前の操作の後、Uの頂点Cと開始点Dの間の距離が最短になります。したがって、CをSに追加し、同時にUの頂点の距離を更新します。頂点Fを例にとると、前のFからDまでの距離は∞ですが、CをSに追加した後、FからDまでの距離は9 =(F、C)+(C、D)です。
このとき、S = {D(0)、C(3)}、U = {A(∞)、B(23)、E(4)、F(9)、G(∞)}。
ステップ3:頂点EをSに追加します。
前の操作の後、Uの頂点Eと開始点Dの間の距離が最短になるため、EがSに追加され、同時にUの頂点の距離が更新されます。頂点Fを例にとると、FからDまでの距離は以前は9でしたが、EをSに追加した後は、FからDまでの距離は6 =(F、E)+(E、D)になります。
このとき、S = {D(0)、C(3)、E(4)}、U = {A(∞)、B(23)、F(6)、G(12)}。
ステップ4:頂点FをSに追加します。
このとき、S = {D(0)、C(3)、E(4)、F(6)}、U = {A(22)、B(13)、G(12)}。
ステップ5:頂点GをSに追加します。
このとき、S = {D(0)、C(3)、E(4)、F(6)、G(12)}、U = {A(22)、B(13)}。
手順6:頂点BをSに追加します。
このとき、S = {D(0)、C(3)、E(4)、F(6)、G(12)、B(13)}、U = {A(22)}。
手順7:頂点AをSに追加します。
このとき、S = {D(0)、C(3)、E(4)、F(6)、G(12)、B(13)、A(22)}。
このとき、始点Dから各頂点までの最短距離が計算されます:A(22)B(13)C(3)D(0)E(4)F(6)G(12)。
2。Golang Dijikstraアルゴリズムを達成するために
func Dijkstra_Alg(D [LEN][LEN]int, v int) {
if v < 0 || v >= LEN{
fmt.Println("错误节点输入!")
return
}
//替换一些原数组中的-1
for i:=0; i<LEN; i++ {
for j:=0; j<LEN; j++ {
if D[i][j] == -1{
D[i][j] = 9999999
}
}
}
//1. 创建S集合
S := make([]int, 0)
S = append(S, v)
//2. 对于当前顶点,做一次迪杰斯特拉算法
//看邻接表对应的那一列
for {
//2.1 找出当前列最小加入到S, 要排除掉已经在S中的点
min := 9999999
index := v
for i:=0; i<LEN; i++{
if !Contains(S, i) && D[i][v] < min{
min = D[i][v]
index = i
}
}
//填充之前判断一下,防止后面都是无穷大
if min == 9999999 && index == v{
break //如果最小的都是∞那么说明后面没有点可以加入了,所以结束
}
fmt.Printf("最小值min=%d, 加入节点%d \n", min, index)
S = append(S, index) //加入目前最小的到S中
//2.2 根据先加入的这个点,更新其他点的权重
for i:=0; i<LEN; i++ {
if !Contains(S, i) && D[i][index] + min < D[i][v]{
D[i][v] = D[i][index] + min
}
}
fmt.Println("集合S:", S)
//2.3 检测下S是否满了,满了就退出,不满就重复前两步
if len(S) >= LEN{
break
}
fmt.Println("===============================")
}
//输出数组看一下
fmt.Println("-----------------Dijkstra算法结果-----------------")
fmt.Println(S)
fmt.Println("-----------------最短路径:-----------------")
fmt.Printf("点%d到各个点的最短路长度是:\n", v)
for i:=0; i<len(S); i++ {
fmt.Printf("(%d, %d) = %d \n", v, S[i], D[S[i]][v])
}
}
プログラムのテスト実行結果:
3.フロイドアルゴリズム理論
フロイドアルゴリズムは、内挿法とも呼ばれます。これは、特定の重み付きグラフ内の複数のソース間の最短経路を見つけるために使用されるアルゴリズムです。アルゴリズムの名前は、創設者の1人であり、1978年にチューリング賞を受賞し、スタンフォード大学のコンピューターサイエンスの教授であるRobertFreudにちなんで名付けられました。
重み行列を使用して、グラフの2点ごとの最短経路行列を見つけます。グラフの重み付き隣接行列A = [a(i、j)] n×nから開始して、n回再帰的に更新します。つまり、行列D(0)= Aから、式に従って、行列Dを作成します。 (1);同じ式を使用してD(1)からD(2)を作成します; ...;最後に、同じ式を使用してD(n-1)から行列D(n)を作成します。行列D(n)の行iと列jの要素は、頂点iから頂点jまでの最短経路長です。D(n)はグラフの距離行列と呼ばれます。同時に、後続のノード行列パスは次のことができます。 2点間の距離を記録するために導入されます。最短経路。リラクゼーション法(リラクゼーション操作)は、iとjの間の他のすべてのポイントでリラクゼーションを実行するために使用されます。したがって、時間計算量はO(n ^ 3)です。
核となるアイデアは、トリプルネストループを介して毎回中間遷移ノードとしてノードを選択し、他の2つのノードペアの間で決定することです。選択したノードを遷移に使用し、取得したパスの長さが短い場合は、ペアノード間のパス距離、つまり更新に対応する距離行列の値。これにより、最も外側のループが選択されたノードを更新ノードとして制御し、2つのネストされたループが任意の2つのノードをトラバーサルとして選択します。選択した中間ノードを遷移して得られる距離が短いかどうかを判断し、次のステップが実行されるたびに最も外側のループが更新されます。つまり、Warshallメソッドで対応する行列の更新が変更されます。
利点:理解しやすく、任意の2つのノード間の最短距離を計算でき、コードを簡単に記述できます。
短所:時間計算量が比較的高く、大量のデータの計算には適していません。
4.フロイドアルゴリズムGoプログラミングの実装
func Floyd_Alg(D [LEN][LEN]int) {
//1. 创建对应的Path矩阵
//1.1 输出原邻接矩阵
fmt.Println("--------------------原来的邻接矩阵--------------------")
PrintMatrix(D[:])
//1.2 创建Path数组
var Path [LEN][LEN]int
for i:=0; i<LEN; i++ {
for j:=0; j<LEN; j++ {
Path[i][j] = -1
}
}
//2. 处理下原邻接矩阵的-1,不影响下面的判断
for i:=0; i<LEN; i++ {
for j:=0; j<LEN; j++ {
if D[i][j] == -1{
D[i][j] = 9999999
}
}
}
//3. 三层循环更新原邻接矩阵和Path矩阵
for v:=0; v<LEN; v++ {
for i:=0; i<LEN; i++ {
for j:=0; j<LEN; j++ {
//制造全排列
if i == j || v == j || v == i{
//三者不能相等,相等则跳过
continue
}
//fmt.Printf("V:%d, (%d, %d)\n", v, i, j)
//如果加上中间点,可以减小距离,那么就更新两个表
if D[i][j] > D[i][v] + D[v][j] {
D[i][j] = D[i][v] + D[v][j] //更新原邻接矩阵
Path[i][j] = v //更新Path路径图
}
}
}
}
//4. 输出邻接矩阵和没两个点之间的最短路径
fmt.Println("--------------------Floyd求得的最短距离邻接矩阵--------------------")
PrintMatrix(D[:])
fmt.Println("-----------------------------------------------------------------")
fmt.Println(D)
//输出每两个点的最短路径
for i:=0; i<LEN; i++ {
for j:=0; j<LEN; j++ {
if i == j {
continue
}
fmt.Printf("(%d, %d)点%d到点%d的最短路径是:", i, j, i, j)
PrintPathMatrix(Path[:], i, j)
fmt.Printf("\n")
}
}
}
実行結果のスクリーンショット:
5.最短経路アルゴリズムの要約
2つの方法の時間計算量はNの3乗であり、大量のデータの計算には適していません。比較すると、DijkstraアルゴリズムとFloydアルゴリズムのアイデアはどちらも「貪欲な」アイデアですが、最短パスです。フロイドアルゴリズムの出力再帰の概念が使用されます。ダイクストラアルゴリズムは、一度に1つのポイントから他のポイントへの最短経路しか見つけることができませんが、フロイドアルゴリズムは、一度に各ポイント間の最短経路を見つけることができます。上記は、2つのアルゴリズムの類似点と相違点です。
参照
CSDN:
https://blog.csdn.net/heroacool/article/details/51014824
https://www.cnblogs.com/skywang12345/p/3711512.html
https://blog.csdn.net/wang_dong001/article/details/50196103