MATLAB-第K个最短路径算法(KSP,K-shortest pathes)
MATLAB代码封装成函数,直接使用。
参考:
MATLAB-K最短路径算法(KSP,K-shortest pathes)_matlab中求k条最短路径-CSDN博客
算法的基本介绍可以看上面的参考文献,这里就不再赘述。
写这篇文章的原因是参考文献的代码有两个问题,第一,子函数没有给出,无法运行程序;第二,会生成重复的路径;第三,在有些样例会产生错误结果。
上面的动图也来自于参考文献。虽然参考文献的代码有很大问题,但是这个动图的思想是对的,我就是依据这个动图所阐释的思想来debug的。
这个就是我的代码:
function [paths, shortestCost,extraCosts] = KSP(graph, source, destination, K, upperBound)
% K Shortest Paths with loop derivation and an upper bound on extra distance
% graph - adjacency matrix representation of the graph, 0 means not
% connected
% source - source node
% destination - destination node
% K - number of shortest paths to find
% upperBound - upper bound for extra distance of derivation path
% Replace 0s with Inf, except on the diagonal
graph(graph == 0) = Inf;
graph(1:size(graph, 1)+1:end) = 0; % Keep the diagonal as 0s
% Define the structure to store paths
output = struct('path', {}, 'cost', {});
% Find the shortest path using Dijkstra's algorithm
[shortestPath, shortestCost] = dijkstra(graph, source, destination);
if isempty(shortestPath)
disp('No path exists between source and destination.');
return;
end
% Initialize the list of k shortest paths
output(1).path = shortestPath;
output(1).cost = shortestCost;
% Initialize the list of potential k-shortest paths
potentialPaths = struct('path', {}, 'cost', {});
for k = 2:K
for i = 1:length(output(k-1).path)-1
% Find the spur node and root path
spurNode = output(k-1).path(i);
rootPath = output(k-1).path(1:i);
% Copy the original graph
tempGraph = graph;
% Remove all edges from the spur node that appear in potential paths
% but preserve those edges that are part of the root path
for j = 1:length(output)
if length(output(j).path) > i && isequal(output(j).path(1:i), rootPath)
nextNode = output(j).path(i+1);
if ~ismember(nextNode, rootPath)
tempGraph(spurNode, nextNode) = Inf;
tempGraph(nextNode, spurNode) = Inf; % if the graph is undirected
end
end
end
% Calculate the spur path from spurNode to destination
[spurPath, spurCost] = dijkstra(tempGraph, spurNode, destination);
% If no spur path is found, continue to the next iteration
if isempty(spurPath)
continue;
end
% Combine the root path and spur path
totalPath = [rootPath spurPath(2:end)];
totalCost = sum(graph(sub2ind(size(graph), totalPath(1:end-1), totalPath(2:end))));
% Add the new path to potential paths list if it satisfies the upper bound
% and is not already present in the paths list
if totalCost <= shortestCost * (1 + upperBound)
isNewPath = true;
for j = 1:length(output)
if isequal(output(j).path, totalPath)
isNewPath = false;
break;
end
end
if isNewPath
potentialPaths(end+1).path = totalPath;
potentialPaths(end).cost = totalCost;
end
end
end
% If there are no more potential paths, break the loop
if isempty(potentialPaths)
break;
end
% Sort potential paths by cost
[~, sortIdx] = sort([potentialPaths.cost]);
potentialPaths = potentialPaths(sortIdx);
% Add the lowest cost potential path to the list of k shortest paths
output(k).path = potentialPaths(1).path;
output(k).cost = potentialPaths(1).cost;
% Remove the chosen path from potential paths list
potentialPaths(1) = [];
end
paths = {};
extraCosts = [];
for i = 1 : size(output,2)
paths{end + 1} = output(i).path;
extraCosts(end + 1) = output(i).cost-shortestCost;
end
end
function [path, cost] = dijkstra(graph, source, destination)
% Dijkstra's algorithm to find the shortest path in a weighted graph
% graph - adjacency matrix representation of the graph
% source - source node
% destination - destination node
numNodes = size(graph, 1);
dist = Inf(1, numNodes);
prev = NaN(1, numNodes);
dist(source) = 0;
Q = 1:numNodes; % Unvisited nodes
while ~isempty(Q)
[~, idx] = min(dist(Q));
u = Q(idx);
Q(idx) = [];
if u == destination
break;
end
neighbors = find(graph(u, :) < Inf);
for v = neighbors
alt = dist(u) + graph(u, v);
if alt < dist(v)
dist(v) = alt;
prev(v) = u;
end
end
end
% Reconstruct the shortest path
path = [];
u = destination;
if ~isnan(prev(u)) || u == source
while ~isnan(u)
path = [u path];
u = prev(u);
end
cost = dist(destination);
else
path = [];
cost = Inf;
end
end
输入格式, graph是图的adjacency matrix用0表示不连接,source和destination是开始点和结尾点的索引,K是生成路径上限
原代码的可读性很差,所以我尝试用chat-GTP3.5生成代码。GTP它给出的函数是直接能跑,有趣的是GTP产生的函数的问题和参考文献的一样。
https://chatgpt.com/share/15044ad2-9687-4ee2-9710-a69503e08bcb
这个是我调教的过程。注意GTP的最终代码不是我的代码,首先,规定输入类型;其次,告诉它输出会有重复路径。然后就是最困难的时刻了。 GTP对下面这个样例生成不了准确的解:
当我尝试对开始点为2,终点为8的O-D对生成三个偏移路径的时候,它会输出三个[2,6,5,8]
参考文献给出的代码可读性没GTP给出的好,我就对GTP的代码进行debug,
解决重复输出的问题,以下都截图自GTP的聊天记录
原来的代码
我告诉它"why do your code generate several paths of the same route?"更改后的代码
发现现在的代码只会生成一个路径[2,6,5,8]了
这串代码错了,在我们的tempGraph中,就像在动图中所说的那样,我们应该移除的是源自spurnode的所有已经尝试过的边,具体表现为开始顶点为spurnode,在potentialPath中出现过的边
指出这个问题后,GTP也修改了,能跑了,耶!