Доброго времени суток, друзья! Сегодня моя копилка реализованных алгоритмов на графах пополнится алгоритмом поиска кратчайших путей между всеми вершинами в графе. Для начала перечислю алгоритмы, которые уже опубликованы на сайте.
— Реализация поиска в ширину на графе;
— Реализация поиска в глубину на графе;
— Реализация топологической сортировки и поиска компонент сильной связности;
Реализация обычного алгоритма Флойда-Уоршелла нужна мне для того, чтобы запрограммировать параллельное вычисление этой задачи с помощью библиотеки MPI. Отчет о результатах распараллеливания я напишу уже совсем скоро(я очень на это надеюсь). А сейчас поговорим о стандартном алгоритме Флойда-Уоршелла.
Суть алгоритма Флойда-Уоршелла
Как я упомянул, алгоритм предназначен для нахождения кратчайших путей между всеми вершинами в графе. По сути он представляет собой простой перебор всех путей и выбор из них наименьшего. Перебор осуществляется по так называемой «матрице смежности» размера NxN, где N — количество вершин графа.
На пересечении i-ой строки и j-го столбца матрицы стоит значение веса ребра из вершины i в вершину j. Главная диагональ матрицы смежности это всегда нули, потому что ребер из i-ой вершины в i-ую в графе быть не должно. В том случае, когда между вершинами нет ребра, в матрице будет стоять условная бесконечность(INF).
Реализация алгоритма Флойда-Уоршелла
Реализуется поиск наименьшего расстояния последовательным перебором всех путей. В три цикла, внешний выбирает вершину, через которую мы пробуем найти лучший путь. Внутренние два перебирают пары вершин, между которыми мы ищем минимальное расстояние.
Матрицу смежности удобнее всего хранить в файле в следующем виде: первой строкой количество вершин, затем непосредственно сама матрица. В качестве бесконечно большого расстояния(INF, когда вершины не связаны ребром) взять конкретную константу. В моей реализации она равна 101, это значит, что максимальный вес ребра не может быть больше 100.
Пример файла с матрицей

4 0 10 1 101 10 0 101 5 1 101 0 2 101 5 2 0
Исходный код алгоритма
//matrix - матрица смежности
void originalFloydWarshall(int **matrix, int numberOfVert) {
//Пробегаемся по всем вершинам и ищем более короткий путь
//через вершину k
for(int k = 0; k < numberOfVert; k++) {
for(int i = 0; i < numberOfVert; i++) {
for(int j = 0; j < numberOfVert; j++) {
//Новое значение ребра равно минимальному между старым
//и суммой ребер i <-> k + k <-> j (если через k пройти быстрее)
matrix[i][j] = min(matrix[i][j], matrix[i][k] + matrix[k][j]);
}
}
}
return;
}
Пример работы с алгоритмом
#include <iostream>
#include <algorithm>
#include <fstream>
//Максимальное значение веса = 100
#define INF 101
using namespace std;
void printMatrix(int** matrix, int numberOfVert) {
for(int i = 0; i < numberOfVert; i++) {
for(int j = 0; j < numberOfVert; j++) {
if(matrix[i][j] == INF) {
cout << "INF" << " ";
}
else {
cout << matrix[i][j] << " ";
}
}
cout << endl;
}
}
//matrix - матрица смежности
void originalFloydWarshall(int **matrix, int numberOfVert) {
//Пробегаемся по всем вершинам и ищем более короткий путь
//через вершину k
for(int k = 0; k < numberOfVert; k++) {
for(int i = 0; i < numberOfVert; i++) {
for(int j = 0; j < numberOfVert; j++) {
//Новое значение ребра равно минимальному между старым
//и суммой ребер i <-> k + k <-> j (если через k пройти быстрее)
matrix[i][j] = min(matrix[i][j], matrix[i][k] + matrix[k][j]);
}
}
}
return;
}
int main(int argc, char** argv) {
ifstream file("matrix");
int numberOfVert;
file >> numberOfVert;
cout << numberOfVert << endl;
//Матрица смежности с весами ребер графа(101 - ребра нет, 0 ребро в себя)
int **matrix = (int**)malloc(sizeof(int)*numberOfVert);
for(int i = 0; i < numberOfVert; i++) {
matrix[i] = (int *) malloc(sizeof(int) * numberOfVert);
}
//Считываем матрицу весов ребер
for(int i = 0; i < numberOfVert; i++) {
for(int j = 0; j < numberOfVert; j++) { file >> matrix[i][j];
}
}
file.close();
cout << "Old matrix" << endl;
printMatrix(matrix, numberOfVert);
originalFloydWarshall(matrix, numberOfVert);
cout << "New matrix" << endl;
printMatrix(matrix, numberOfVert);
return 0;
}
Заключение
Обычный алгоритм Флойда-Уоршелла реализуется очень просто, но работает очень долго на больших матрицах. Идеальный кандидат для того, чтобы отдать его на растерзание библиотеке MPI. На сегодня у меня все, спасибо за внимание!