経路計画アルゴリズム シリーズ: (ダイクストラ)



序文

このシリーズの記事は主に、経路計画アルゴリズムの学習過程におけるいくつかの知識ポイントを記録しています。主な学習ビデオは、
Bilibili University小黎的Allyの「Path Planning and Trajectory Tracking Series Algorithms」からのものです。ビデオ リンクは次のとおりです:ビデオ リンク
記事参照:リンク 1リンク 2


ここに画像の説明を挿入

1. ディクストラ

アルゴリズムの紹介

  • ダイクストラのアルゴリズムは、ノードから他のノードへの最短パスを計算するために使用される典型的な最短パス アルゴリズムです。

主な特徴:

  • 始点を中心として、終点に至るまで外層へ拡張していく(幅優先探索の考え方)。
  • ダイクストラのアルゴリズムは、幅優先探索を使用して、重み付き有向グラフまたは無向グラフにおける単一ソース最短経路問題を解決します。

基本的な考え方

ここに画像の説明を挿入

ダイクストラを介してグラフ内の最短パスを計算する場合、開始点 s を指定する必要があります (つまり、計算はソース点 s から開始されます)。
さらに、2 つのセット S と U が導入されます。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」を選択し、S に頂点 k を追加すると同時に、U から頂点 k を削除します。
(3) U の各頂点から始点 s までの距離を更新します。U の頂点の距離が更新される理由は、前のステップで k が最短パスの頂点であると決定され、k を使用して他の頂点の距離 (たとえば、次の頂点の距離) を更新できるためです。 (s, v) は、(s, v) k)+(k,v) の距離よりも大きい場合があります。
(4) すべての頂点を通過するまで、手順 (2) と (3) を繰り返します。
上記の理論だけを見ると理解するのが難しいかもしれませんが、次の例でアルゴリズムを説明します。

2. 図

ここに画像の説明を挿入
ここに画像の説明を挿入

初期状態: S は最短経路が計算された頂点のセット、U は最短経路が計算されていない頂点のセットです。

ステップ 1: ソースポイントを選択する

ソース ポイント D を選択して S セットに追加します。
このとき、S={D(0)}、U={A(∞)、B(∞)、C(3)、E(4)、F(∞)、G(∞)}となります。注: C(3) は、C から開始点 D までの距離が 3 であることを意味し、∞は距離が不明であることを意味します。

ステップ 2: 最短距離の点を見つけ、S セットに参加し、U セットを更新します

U セットから選択距离最短的节点C
S セットにノード C を追加し、同時に U セットから C を削除します。
前の操作の後、U のノード C からソース点 D までの距離が最も短いため、S に C を加算し、同時に U のノードの距離を更新します。ノード F を例にとると、F から D までの距離は ∞ ですが、S に C を加算すると、F から D までの距離は 9=(F,C)+(C,D) となります。
このとき、S={D(0)、C(3)}、U={A(∞)、B(23)、E(4)、F(9)、G(∞)}となる。

ステップ 3: U セットの最小距離ポイントを選択し、S セットに結合し、U セットを更新します

ノード E を S に追加します。
前の操作の後、U のノード E からソース点 D までの距離が最も短いため、S に E を加算し、同時に U のノードの距離を更新します。引き続きノード F を例にとると、F から D までの距離は 9 でしたが、E を S に加算すると、F から D までの距離は 6=(F,E)+(E,D) になります。したがって、点 F の距離を更新します;
このとき、 S={D(0),C(3),E(4)}, U={A(∞),B(13),F(6),G (12) }。

ステップ 4: U セットの最小距離ポイントを選択し、S セットに結合し、U セットを更新します

ノード F を S に追加します。
このとき、S={D(0)、C(3)、E(4)、F(6)}、U={A(22)、B(13)、G(12)}となる。

ステップ 5: U セットの最小距離ポイントを選択し、S セットに結合し、U セットを更新します

ノード G を S に追加します。
このとき、S={D(0)、C(3)、E(4)、F(6)、G(12)}、U={A(22)、B(13)}となる。

ステップ 6: U セットの最小距離ポイントを選択し、S セットに結合し、U セットを更新します

ノード B を S に追加します。
このとき、S={D(0)、C(3)、E(4)、F(6)、G(12)、B(13)}、U={A(22)}となる。

ステップ 7: U セットの最小距離ポイントを選択し、S セットに結合し、U セットを更新します

ノード A を S に追加します。
このとき、S={D(0)、C(3)、E(4)、F(6)、G(12)、B(13)、A(22)}となる。U={空集合}
この時点で、ソース点 D から各ノードまでの最短距離が計算されます: A(22) B(13) C(3) D(0) E(4) F(6) G( 12)。つまり、D→A の最適パスは D→E→F→A で、最短距離は 22 です。

3. プログラミングの実装

コードは以下のように表示されます。

#include<iostream>
#include<string>
using namespace std;

const int MAX = 10; //限定最大的顶点数
const int _INFINITY = 65535; //定义无穷大
class Graph
{
    
    
private:
	int vertex_num; //顶点数
	int edge_num; //边数
	int weight; //权值
	string vertex[MAX]; //顶点数组
	int edge[MAX][MAX]; //邻接矩阵
	int locate(string ch); //定位
	int *final; //标识是否已纳入最短路径,置为1表示纳入
	int *distance; //存放最短路径的权值和
	string *path;  //存放最短路径字符串

public:
	Graph(int v_n, int e_n); //构造函数
	void print_graph(); //打印邻接矩阵
	void Dijkstra(int v); //Dijkstra算法
	int Min(int *_distance, int num); //最小值
};

//定位(找到字母的下标)
int Graph::locate(string ch)
{
    
    
	int index;
	for (index = 0; index < this->vertex_num; index++)
	{
    
    
		if (this->vertex[index] == ch)
		{
    
    
			break;
		}
	}
	return index;
}

//构造函数
Graph::Graph(int v_n, int e_n) : vertex_num(v_n), edge_num(e_n)
{
    
    
	int i, j, k;
	cout << "现在请输入这" << this->vertex_num << "个顶点:" << endl;
	for (i = 0; i < this->vertex_num; i++)
	{
    
    
		cin >> this->vertex[i];
	}

	//初始化邻接矩阵
	for (i = 0; i < this->vertex_num; i++)
	{
    
    
		for (j = 0; j < this->vertex_num; j++)
		{
    
    
			if (i == j)
			{
    
    
				this->edge[i][j] = 0;
			}
			else
			{
    
    
				this->edge[i][j] = _INFINITY;
			}

		}
	}

	cout << "请依次输入边相邻的两个顶点及边的权值:" << endl;
	for (k = 0; k < this->edge_num; k++)
	{
    
    
		string first, second;
		cin >> first >> second >> this->weight;
		i = this->locate(first);
		j = this->locate(second);

		edge[i][j] = this->weight;
		//无向图
		edge[j][i] = this->weight;
	}
}

//打印
void Graph::print_graph()
{
    
    
	cout << "邻接矩阵为:" << endl;
	for (int i = 0; i < this->vertex_num; i++)
	{
    
    
		for (int j = 0; j < this->vertex_num; j++)
		{
    
    
			cout << this->edge[i][j] << "\t";
		}
		cout << endl;
	}
}

//返回最小值下标
int Graph::Min(int *_distance, int num)
{
    
    
	int index = 0, min = _INFINITY;
	for (int i = 0; i < num; i++)
	{
    
    
		if (!this->final[i] && _distance[i] < min)
		{
    
    
			min = _distance[i];
			index = i;
		}
	}
	return index;
}

//Dijkstra算法
void Graph::Dijkstra(int v)  //从源点v出发
{
    
    
	int i, j, min;
	this->path = new string[MAX];
	this->final = new int[MAX];
	this->distance = new int[MAX];
	//初始化上列数组
	for (i = 0; i < this->vertex_num; i++)
	{
    
    
		this->final[i] = 0;
		this->distance[i] = this->edge[v][i]; //初始化为源点到各t'hi点的权值大小
		if (this->distance[i] != _INFINITY)
		{
    
    
			this->path[i] = this->vertex[v] + this->vertex[i]; //当直接路径存在时,同样初始化为源点到各点的路径
		}
		else
		{
    
    
			this->path[i] = "";
		}
	}
	//初始化源点
	this->distance[v] = 0;
	this->final[v] = 1;

	//开始主循环,每次求得v到一个顶点的最短路径,所以循环次数比顶点数少一次
	for (i = 1; i < this->vertex_num; i++)
	{
    
    
		min = this->Min(distance, this->vertex_num); //求当前最小值下标
		cout << "最短路径:" << this->path[min] << "\t权值:" << this->distance[min] << endl; //输出当前最短路径
		this->final[min] = 1;
		//再次循环,修正当前最短路径及其权值和
		for (j = 0; j < this->vertex_num; j++)
		{
    
    
			//如果经过的顶点的路径比现在这条路径还短的话
			if (!this->final[j] && this->distance[j] > this->edge[min][j] + this->distance[min])
			{
    
    
				this->distance[j] = this->edge[min][j] + this->distance[min];
				this->path[j] = this->path[min] + this->vertex[j];
			}
		}
	}
	delete[]path;
	delete[]distance;
	delete[]final;
}


演算結果

ここに画像の説明を挿入


要約する

ダイクストラのアルゴリズムは、実際には、各ステップで U セットの最小距離の値を見つけ、それを S セットに入れ、U セットのデータを更新し、すべてのノードを走査した後、最後に最小値を見つけることです。それが最適なパスです。
最後に、アルゴリズムは最適なパス ツリーを取得でき、複雑さはより合理的になります (O(N^2))。
ダイクストラ アルゴリズムのエッジの重みはすべて正の数であるため、負の重み付けはありません。したがって、最短距離を持つ現在の点は他の点によって更新できません。つまり、各チェックが最短距離の点であることが保証されます。 。
より短い距離の点が選択されるたびに、他の点の距離が更新されます。ある地点に直接行くことは、遠回りするほど近くないかもしれないからです。
ダイクストラ アルゴリズムのエッジの重みはすべて正の数です。負の数がある場合は、ベルマン フォード アルゴリズムが必要です。任意の 2 点間の最短距離が必要な場合は、フロイド アルゴリズムを選択してください。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/TianHW103/article/details/127521312