MATLAB-第K个最短路径算法(KSP,K-shortest pathes),附AI训练过程

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也修改了,能跑了,耶!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值