当前位置:   article > 正文

个人数学建模算法库之图的最短路径模型_数学建模最短路径模型

数学建模最短路径模型

图论术语

路的长度:赋权图的路经过的边的权和

最短路:从顶点 v i v_i vi到顶点 v j v_j vj的路中,长度最小的一条路。

顶点间的距离:顶点 v i v_i vi到顶点 v j v_j vj的最短路的长度。记作 d ( u 0 , v 0 ) d(u_0,v_0) d(u0,v0)

固定起点的最短路

模型简介

已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)

固定顶点 u o u_o uo ,求 u 0 u_0 u0到其余顶点 v v v的最短路及距离 d ( u 0 , v ) d(u_0,v) d(u0,v)

Dijkstra(迪克斯特拉)算法

算法思想:最短路径的子路也是最短路。固定 u 0 u_0 u0,从近到远求出 u 0 u_0 u0到其余顶点之一 v v v的最短路和距离,直至遍历完所有顶点(或到某一特定顶点 v 0 v_0 v0 )。

记号说明

记号含义
d n o w ( v ) d_{now}(v) dnow(v)从顶点 u 0 u_0 u0当前顶点 v v v的路径长度
p a r e n t s ( v ) parents(v) parents(v)最短路径中当前顶点 v v v的上一个顶点
S i S_i Si含i个顶点的最短路径顶点集
算法步骤
  • d n o w ( u 0 ) = 0 d_{now}(u_0)=0 dnow(u0)=0.对于其他顶点 v ≠ u 0 v\neq u_0 v=u0 ,令 d n o w ( v ) = ∞ d_{now}(v)=\infty dnow(v)= , p a r e n t s ( v ) = u 0 parents(v)=u_0 parents(v)=u0 .令 S 0 = { u 0 } S_0=\{u_0\} S0={u0}, i = 0 i=0 i=0
  • 对于所有的 v ∉ S i v\notin S_i v/Si ,令 ( v ) = min ⁡ u ∈ S i { d n o w ( v ) , d n o w ( u ) + w ( u v ) } \displaystyle (v)=\min_{u\in S_i}\{d_{now}(v),d_{now}(u)+w(uv)\} (v)=uSimin{dnow(v),dnow(u)+w(uv)}.若通过 u u u更新了 d n o w ( v ) d_{now}(v) dnow(v),则令 p a r e n t s ( v ) = u parents(v)=u parents(v)=u;否则不变。
  • 寻找 min ⁡ v ∉ S i d n o w ( v ) \displaystyle \min_{v\notin S_i} d_{now}(v) v/Simindnow(v) ,记该顶点为 u i + 1 u_{i+1} ui+1,令 S i + 1 = S i ∪ { u i + 1 } S_{i+1}=S_i\cup\{u_{i+1}\} Si+1=Si{ui+1}
  • i = ∣ V ∣ − 1 i=|V|-1 i=V1或者 v 0 v_0 v0进入 S i S_i Si,算法终止;否则重复2,3步

算法结束后, S i S_i Si为最短路径集。 d n o w ( v ) d_{now}(v) dnow(v)为距离集。

算法局限:要求权值为正数

Matlab代码
% Dijkstra.m

function [path, distance] = Dijkstra(G, u0, v0)
    % Dijkstra算法求最短路径

    % 初始化参数
    n = G.numnodes;
    nodes = 1:n;
    i = 0;
    d_now = ones(n, 1) * inf;
    d_now(u0) = 0;
    parents = ones(n, 1) * u0;
    in_s = u0;

    history = [];
    % 遍历所有终点或v0进入最短路径集
    while (i ~= n - 1) && ~(ismember(v0, in_s))
        not_in_s = setdiff(nodes, in_s);

        for v = not_in_s

            for u = in_s
                % 查找uv间的边
                uv_index = G.findedge(u, v);

                if uv_index
                    % 边存在则获取权
                    w = min(G.Edges.Weight(uv_index));
                    % 计算新的最短距离
                    d_now(v) = min([d_now(v), d_now(u) + w]);

                    if d_now(u) + w <= d_now(v)
                        % 距离发生改变
                        parents(v) = u;
                        % 记录变化
                        history = [history; [u, v]];
                    end

                end

            end

        end

        % 寻找最短距离的顶点
        tmp = d_now;
        tmp(in_s) = inf;
        [~, new_u] = min(tmp);
        new_u = new_u(1);
        % 添加最短路径顶点
        in_s = union(new_u, in_s);
        i = i + 1;
    end

    % 倒溯最短路径
    last = v0;
    path = v0;

    while last ~= u0
        k = find(history(:, 2) == last);
        last = history(k(end), 1);
        path = [last, path];
    end

    distance = d_now(v0);
end

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

示例:

clc,clear

% 输入图的顶点对
E=[ 1 2 2;1 3 8;1 4 1;
    2 3 6;2 5 1;3 4 7;
    3 5 5;3 6 1;3 7 2;
    4 7 9;5 6 3;5 8 2;
    5 9 9;6 7 4;6 9 6;
    7 9 3;7 10 1;8 9 7;
    8 11 9;9 10 1;9 11 2;
    10 11 4];

% 创建并绘制图
G=graph(E(:,1),E(:,2),E(:,3));
figure=plot(G,"EdgeLabel",G.Edges.Weight,'EdgeLabelColor','green');

% 使用Dijkstra算法求得最短路径并在图中标出
[path,distance]=Dijkstra(G,1,11);
highlight(figure,path,'EdgeColor','red','LineWidth',2,"EdgeLabelColor","red");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

Python代码
def Dijkstra(G, u0, v0):
    """利用Dijkstra计算最短路径和距离"""

    # 初始化参数
    n = G.number_of_nodes()
    nodes = list(range(1, n + 1))
    d_now = np.ones(n) * np.inf
    d_now[u0 - 1] = 0
    parents = [u0 for i in range(n)]
    s = set()
    s.add(u0)
    history = []
    i = 0

    # 遍历所有终点或v0进入最短路径顶点集
    while (i != n - 1) or (v0 not in s):
        not_s = set(nodes) - s
        for v in not_s:
            for u in s:
                try:
                    # 检查顶点u,v间是否有边
                    w = G.edges[u, v]['weight']
                except:
                    pass
                else:
                    d_now[v - 1] = min([d_now[v - 1], d_now[u - 1] + w])
                    if d_now[u - 1] + w <= d_now[v - 1]:
                        # 顶点最短值改变,记录改变
                        parents[v - 1] = u
                        history.append([u, v])

        # 寻找当前的最小距离点,并加入s集合
        tmp = d_now.copy()
        in_s_index = [i - 1 for i in s]
        tmp[in_s_index] = np.inf
        new_u = np.argmin(tmp) + 1
        s.add(new_u)
        i = i + 1

    # 拼接最终的最短路径
    last = v0
    path = [v0]
    history = np.array(history)
    while last != u0:
        k = np.where(history[:, 1] == last)
        last = history[k[-1][-1], 0]
        path.append(last)

    path = path[-1::-1]
    distance = d_now[v0 - 1]
    return path, distance
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

示例:

# 导库
import networkx as nx
import numpy as np

# 输入边和权
edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7),
         (3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2),
         (5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7),
         (8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)]

# 创建空图并添加顶点和边权
G = nx.Graph()
G.add_weighted_edges_from(edges)

# 计算顶点位置
pos = nx.spring_layout(G)

# 绘制无权图
nx.draw(G, pos, with_labels=True, font_size=14)

# 绘制最短路径
path,distance=Dijkstra(G,1,11)
shortest_edges=[]
for i in range(np.size(path)-1):
    shortest_edges.append((path[i],path[i+1]))
nx.draw_networkx_edges(G,pos,edgelist=shortest_edges,edge_color='red')

# 追加绘制权
labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos,edge_labels=labels,font_color="green")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在这里插入图片描述

所有顶点间的最短路

模型简介

已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)

求所有顶点对 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)之间的最短距离及对应最短路径

Floyd(弗洛伊德)算法

算法思想:依次将每个顶点作为可能的中间点,插入所有的顶点对之间进行判断,若最短距离更新,则将该点记录下来。直到n次之后,得到所有顶点对的最短距离,并通过之间的记录反推最短路径。

记号说明

记号含义
d i s t a n c e s distances distances距离变换矩阵。算法结束后则为顶点之间的最短距离矩阵
R R R路由矩阵。记录顶点间的中间点变化
算法步骤
  • 初始化:令 d i s t a n c e s distances distances为图 G G G的带权邻接矩阵.设置不相邻的顶点对对应的 d i s t a n c e s distances distances元素为 ∞ \infty .令 R = 0 n × n R=0_{n\times n} R=0n×n . R R R对应位置的元素为0,代表两顶点最短路径间无中间点。
  • 求距离矩阵和路由矩阵:从顶点 v 1 v_1 v1开始直到 v n v_n vn,取该顶点 v k v_k vk,依次插入到顶点对 ( v i , v j ) (v_i,v_j) (vi,vj)间,若 d i s t a n c e s ( i , k ) + d i s t a n c e s ( k , j ) < d i s t a n c e s ( i , j ) distances(i,k)+distances(k,j)<distances(i,j) distances(i,k)+distances(k,j)<distances(i,j),说明 v k v_k vk ( v i , v j ) (v_i,v_j) (vi,vj)最短路径的中间点。更新 d i s t a n c e s ( i , j ) distances(i,j) distances(i,j) d i s t a n c e s ( i , k ) + d i s t a n c e s ( k , j ) distances(i,k)+distances(k,j) distances(i,k)+distances(k,j).并令 R ( i , j ) = k R(i,j)=k R(i,j)=k,记录下该中间点。
  • 复原路径:要获得 v i v_i vi v j v_j vj的最短路径,只需找到其中间点 v R ( i , j ) v_{R(i,j)} vR(i,j) .再寻找 v i v_i vi v R ( i , j ) v_{R(i,j)} vR(i,j)的中间点, v j v_j vj v R ( i , j ) v_{R(i,j)} vR(i,j)的中间点…重复该过程。

算法局限:所有圈的权值和非负

Matlab代码
% Floyd.m

function [distances, paths] = Floyd(G)
    % Floyd算法求所有顶点对之间的最短路径

    % 矩阵初始化
    distances = G.adjacency('weighted');
    n = length(distances);
    distances(distances == 0) = inf;
    distances(1:n + 1:end) = 0;
    R = zeros(n);

    for k = 1:n

        for i = 1:n

            for j = 1:n

                % 插点更新
                if distances(i, k) + distances(k, j) < distances(i, j)
                    distances(i, j) = distances(i, k) + distances(k, j);
                    R(i, j) = k;
                end

            end

        end

    end

    distances = full(distances);

    % 复原路径
    paths = returnPaths(R);

    for i = 1:n

        for j = 1:n

            if i ~= j
                % 添加起点和终点
                paths{i, j} = [i paths{i, j} j];
            else
                paths{i, j} = i;
            end

        end

    end

end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
% returnPaths.m

function paths = returnPaths(R)
    % 根据路由矩阵返回最短路径矩阵
    n = length(R);
    paths = cell(n);

    for i = 1:n

        for j = 1:n
            paths{i, j} = parse(i, j);
        end

    end
    
    % 递归推导最短路径
    function path = parse(i, j)
        p = R(i, j);

        if p == 0
            path = [];
        else
            left = parse(i, p);
            right = parse(p, j);
            path = [left, p, right];
        end

    end

end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

示例:

clear,clc

E=[ 1 2 2;1 3 8;1 4 1;
    2 3 6;2 5 1;3 4 7;
    3 5 5;3 6 1;3 7 2;
    4 7 9;5 6 3;5 8 2;
    5 9 9;6 7 4;6 9 6;
    7 9 3;7 10 1;8 9 7;
    8 11 9;9 10 1;9 11 2;
    10 11 4];

G=graph(E(:,1),E(:,2),E(:,3));

n=G.numnodes;

[distances,paths]=Floyd(G)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

在这里插入图片描述

Python代码
def returnPaths(R):
    """根据路由矩阵还原路径"""
    def parse(i, j):
        """递归解析i,j顶点最短路径"""
        p = R[i, j]
        if p == 0:
            path = []
        else:
            path = [p]
            left = parse(i, p - 1)
            right = parse(p - 1, j)
            path.extend(right)
            for num in left[-1::-1]:
                path.insert(0, num)
        return path

    n, n = np.shape(R)
    paths = [[] for i in range(n)]
    for i in range(n):
        for j in range(n):
            paths[i].append(parse(i, j))
    return paths


def Floyd(G):
    """Floyd算法求所有顶点间的最短路径"""

    # 初始化矩阵
    distances = nx.to_numpy_matrix(G)
    n, n = np.shape(distances)
    distances[distances == 0] = np.inf
    row, col = np.diag_indices_from(distances)
    distances[row, col] = 0
    R = np.zeros((n, n), dtype=int)

    # 插点更新
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if distances[i, k] + distances[k, j] < distances[i, j]:
                    distances[i, j] = distances[i, k] + distances[k, j]
                    R[i, j] = k + 1

    # 复原路径
    paths = returnPaths(R)

    # 路径添加起点和终点
    for i in range(n):
        for j in range(n):
            if i != j:
                paths[i][j].append(j + 1)
                paths[i][j].insert(0, i + 1)
            else:
                paths[i][j] = [i + 1]
    return distances, paths
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

示例:

# 导库
import networkx as nx
import numpy as np
import pandas as pd

# 输入边和权
edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7),
         (3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2),
         (5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7),
         (8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)]

# 创建空图并添加顶点和边权
G = nx.Graph()
G.add_weighted_edges_from(edges)

# Floyd算法
distances, paths = Floyd(G)

n = G.number_of_nodes()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
d_df = pd.DataFrame(distances, index=range(1, n + 1), columns=range(1, n + 1))
d_df
  • 1
  • 2

在这里插入图片描述

p_df = pd.DataFrame(paths, index=range(1, n + 1), columns=range(1, n + 1))
p_df
  • 1
  • 2

在这里插入图片描述

库函数

Matab版

[path,distance] = shortestpath(G,u0,v0) # 求u0到v0的最短路径和最短距离

D=distances(G) # 求顶点对间的最短距离矩阵
  • 1
  • 2
  • 3

Python版

import networkx as nx
import pandas as pd

path = nx.dijkstra_path(G, u0, v0)  # Dijkstra算法求两点的最短路径列表
distance = nx.dijkstra_path_length(G, u0, v0)  # Dijkstra算法求两点的最短距离

path = nx.shortest_path(G, u0, v0,weight='weight')  # 求两点的最短路径列表
distance = nx.shortest_path_length(G, u0, v0,weight='weight') # 求两点的最短距离

distances = dict(nx.all_pairs_dijkstra_path_length(G))  # 求顶点对之间的最短距离字典
d_df = pd.DataFrame(distances).T
d_df

paths = dict(nx.all_pairs_dijkstra_path(G)) # 求顶点对之间的最短路径字典
p_df = pd.DataFrame(paths).T
p_df
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/969956
推荐阅读
相关标签
  

闽ICP备14008679号