专栏:数学建模学习笔记
一、图与网络的基本概念
1. 无向图与有向图
无向图:由顶点集合和边集合组成,边是无方向的。表示为 G=(V,E),其中 V 是顶点集合, E 是边集合。有向图:边是有方向的,从一个顶点指向另一个顶点。表示为 G=(V,E),其中每条边用有序对表示。2. 简单图、完全图、赋权图
简单图:无重复边和自环的图。完全图:每对顶点之间都有边的图, Kn 表示n个顶点的完全图。赋权图:边带有权值的图,用于表示边的某种属性,如距离、费用等。3. 顶点的度
度:与顶点相连的边的数目。在无向图中,顶点的度即为相连边的数目。在有向图中,入度为指向该顶点的边数,出度为从该顶点出发的边数。4. 子图与连通性
子图:由原图的一部分顶点和边组成的图。连通性:图中任意两顶点之间存在路径。无向图称为连通图,有向图称为强连通图。5. 图的矩阵表示
邻接矩阵:表示顶点之间是否有边的矩阵。若顶点 i 和顶点 j 之间有边,则矩阵A[i][j] 为1,否则为0。关联矩阵:表示顶点和边关系的矩阵。若边ek 连接顶点 vi 和 vj,则矩阵 A[i][k] 和]A[j][k] 分别为1。MATLAB代码实例
% MATLAB代码% 创建无向图和有向图的邻接矩阵表示% 无向图的邻接矩阵A_undirected = [0 1 1 0; 1 0 1 1; 1 1 0 1; 0 1 1 0];% 有向图的邻接矩阵A_directed = [0 1 0 0; 0 0 1 0; 0 0 0 1; 1 0 0 0];% 显示邻接矩阵disp('无向图的邻接矩阵:');disp(A_undirected);disp('有向图的邻接矩阵:');disp(A_directed);% 创建图对象并可视化G_undirected = graph(A_undirected);G_directed = digraph(A_directed);% 绘制图figure;subplot(1,2,1);plot(G_undirected);title('无向图');subplot(1,2,2);plot(G_directed);title('有向图');
无向图的邻接矩阵: 0 1 1 0 1 0 1 1 1 1 0 1 0 1 1 0有向图的邻接矩阵: 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0
邻接矩阵表示无向图的邻接矩阵:A_undirected 是一个4x4矩阵,其中 A_undirected(i, j) 表示顶点 i 和顶点 j 之间是否有边。对于无向图,矩阵是对称的。有向图的邻接矩阵:A_directed 是一个4x4矩阵,其中 A_directed(i, j) 表示顶点 i 到顶点 j 之间是否有边。对于有向图,矩阵不是对称的。显示邻接矩阵使用 disp 函数显示无向图和有向图的邻接矩阵,以便于检查矩阵的正确性。创建图对象并可视化graph(A_undirected) 创建无向图对象。digraph(A_directed) 创建有向图对象。使用 plot 函数可视化图对象。subplot 函数将图分成两部分,分别显示无向图和有向图。
Python代码实例
import networkx as nximport matplotlib.pyplot as pltimport numpy as npfrom matplotlib.font_manager import FontProperties# 设置中文字体font = FontProperties(fname=r"C:\Windows\Fonts\simsun.ttc", size=15)# 无向图的邻接矩阵A_undirected = np.array([ [0, 1, 1, 0], [1, 0, 1, 1], [1, 1, 0, 1], [0, 1, 1, 0]])# 有向图的邻接矩阵A_directed = np.array([ [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])# 使用邻接矩阵创建无向图和有向图G_undirected = nx.from_numpy_array(A_undirected)G_directed = nx.from_numpy_array(A_directed, create_using=nx.DiGraph)# 绘制无向图plt.figure(figsize=(10, 5))plt.subplot(1, 2, 1)nx.draw(G_undirected, with_labels=True, node_color='skyblue', edge_color='black', node_size=1500, font_size=20)plt.title('无向图', fontproperties=font)# 绘制有向图plt.subplot(1, 2, 2)nx.draw(G_directed, with_labels=True, node_color='lightgreen', edge_color='red', node_size=1500, font_size=20, arrows=True)plt.title('有向图', fontproperties=font)plt.show()
注意的细节知识点邻接矩阵表示确保邻接矩阵的对称性(对于无向图)。邻接矩阵的大小应为 n x n,其中 n 是顶点数量。显示矩阵使用 disp 或 print 函数显示矩阵,便于检查矩阵的正确性。创建图对象使用 graph 和 digraph 函数分别创建无向图和有向图对象。确保输入的邻接矩阵符合图的类型(无向或有向)。可视化图使用 plot 函数可视化图对象,便于直观理解图的结构。在Python中,可以使用 networkx 库中的 draw 函数进行可视化,matplotlib 库中的 subplot 函数分割图形窗口。MATLAB和Python的差异MATLAB中的 graph 和 digraph 函数对应于Python中的 nx.Graph 和 nx.DiGraph。MATLAB中的 disp 函数对应于Python中的 print 函数。MATLAB中的 plot 函数对应于Python中的 nx.draw 函数。
二、最短路径问题
1. 最短路径问题的定义
最短路径问题是寻找从一个顶点到另一个顶点的路径,使得路径上的边权值和最小。2. Dijkstra算法
适用于非负权图的单源最短路径问题。算法步骤: 初始化:设置起点到自己的距离为0,其他顶点为无穷大。选择未处理的顶点中距离最小的顶点,将其标记为已处理。更新该顶点的邻接顶点的距离。重复步骤2和3,直到所有顶点都被处理。MATLAB代码实例
% MATLAB代码% Dijkstra算法实现function [dist, path] = dijkstra(A, start_node) n = size(A, 1); % 顶点数量 dist = inf(1, n); % 初始化距离 dist(start_node) = 0; visited = false(1, n); % 访问标记 path = -ones(1, n); % 路径 for i = 1:n % 选择未处理顶点中距离最小的顶点 [~, u] = min(dist + visited * inf); visited(u) = true; % 更新邻接顶点的距离 for v = 1:n if A(u, v) > 0 && ~visited(v) alt = dist(u) + A(u, v); if alt < dist(v) dist(v) = alt; path(v) = u; end end end endend% 示例图的邻接矩阵A = [0 10 20 0 0; 10 0 5 1 0; 20 5 0 2 3; 0 1 2 0 4; 0 0 3 4 0];% 计算从起点1到其他顶点的最短路径[start_node] = 1;[dist, path] = dijkstra(A, start_node);% 显示结果disp('顶点的最短距离:');disp(dist);disp('最短路径:');disp(path);% 可视化结果G = digraph(A);figure;h = plot(G, 'EdgeLabel', G.Edges.Weight);highlight(h, path, 'EdgeColor', 'r', 'LineWidth', 2);title('Dijkstra最短路径');
顶点的最短距离: 0 10 20 Inf Inf最短路径: -1 1 1 -1 -1
初始化n = size(A, 1):获取图中顶点的数量。dist = inf(1, n):初始化每个顶点的距离为无穷大。dist(start_node) = 0:起点到自身的距离为0。visited = false(1, n):初始化访问标记。path = -ones(1, n):初始化路径数组。主循环for i = 1:n:对每个顶点进行迭代。[~, u] = min(dist + visited * inf):选择未处理顶点中距离最小的顶点。visited(u) = true:将该顶点标记为已处理。更新邻接顶点的距离for v = 1:n:对每个邻接顶点进行迭代。if A(u, v) > 0 && ~visited(v):如果顶点 v 与顶点 u 之间有边且未被访问。alt = dist(u) + A(u, v):计算通过顶点 u 到顶点 v 的距离。if alt < dist(v):如果通过 u 到 v 的距离小于当前已知的 v 的距离。dist(v) = alt:更新 v 的距离。path(v) = u:更新 v 的路径。
Python代码实例
import heapqdef dijkstra(A, start_node): n = len(A) dist = [float('inf')] * n dist[start_node] = 0 visited = [False] * n path = [-1] * n queue = [(0, start_node)] while queue: d, u = heapq.heappop(queue) if visited[u]: continue visited[u] = True for v, weight in enumerate(A[u]): if weight > 0 and not visited[v]: alt = dist[u] + weight if alt < dist[v]: dist[v] = alt path[v] = u heapq.heappush(queue, (alt, v)) return dist, path# 示例图的邻接矩阵A = [ [0, 10, 20, 0, 0], [10, 0, 5, 1, 0], [20, 5, 0, 2, 3], [0, 1, 2, 0, 4], [0, 0, 3, 4, 0]]# 计算从起点0到其他顶点的最短路径start_node = 0dist, path = dijkstra(A, start_node)# 显示结果print('顶点的最短距离:', dist)print('最短路径:', path)
顶点的最短距离: [0, 10, 13, 11, 15]最短路径: [-1, 0, 3, 1, 3]
三、最小生成树问题
1. 最小生成树的定义
最小生成树(MST)是包含图中所有顶点且边权值和最小的生成树。2. Kruskal算法
适用于任意图。算法步骤: 将图中的边按权值从小到大排序。依次选择权值最小的边,若该边的加入不形成圈,则将其加入生成树。重复步骤2,直到生成树包含所有顶点。MATLAB代码实例
% MATLAB代码% Kruskal算法实现function [mst_edges, mst_weight] = kruskal(A) n = size(A, 1); edges = []; for i = 1:n for j = i+1:n if A(i, j) > 0 edges = [edges; i, j, A(i, j)]; end end end edges = sortrows(edges, 3); parent = 1:n; mst_edges = []; mst_weight = 0; function p = find(parent, x) if parent(x) == x p = x; else p = find(parent, parent(x)); parent(x) = p; end end for k = 1:size(edges, 1) u = edges(k, 1); v = edges(k, 2); w = edges(k, 3); pu = find(parent, u); pv = find(parent, v); if pu ~= pv mst_edges = [mst_edges; u, v]; mst_weight = mst_weight + w; parent(pu) = pv; end endend% 示例图的邻接矩阵A = [0 10 6 5; 10 0 0 15; 6 0 0 4; 5 15 4 0];% 计算最小生成树[mst_edges, mst_weight] = kruskal(A);% 显示结果disp('最小生成树的边:');disp(mst_edges);disp('最小生成树的权重:');disp(mst_weight);% 可视化结果G = graph(A);figure;h = plot(G, 'EdgeLabel', G.Edges.Weight);highlight(h, mst_edges(:,1), mst_edges(:,2), 'EdgeColor', 'r', 'LineWidth', 2);title('Kruskal最小生成树');
最小生成树的边: 3 4 1 4 1 2最小生成树的权重: 19
初始化n = size(A, 1):获取图中顶点的数量。edges = []:初始化边的列表。使用嵌套循环遍历邻接矩阵,收集所有边的信息。边的排序edges = sortrows(edges, 3):按边的权值从小到大排序。并查集的初始化parent = 1:n:初始化每个顶点的父节点为自身。mst_edges = []:初始化最小生成树的边列表。mst_weight = 0:初始化最小生成树的权重和。并查集查找函数function p = find(parent, x):递归查找顶点的根节点,并进行路径压缩。
Python代码实例
class UnionFind: def __init__(self, n): self.parent = list(range(n)) def find(self, u): if self.parent[u] != u: self.parent[u] = self.find(self.parent[u]) return self.parent[u] def union(self, u, v): root_u = self.find(u) root_v = self.find(v) if root_u != root_v: self.parent[root_u] = root_vdef kruskal(A): n = len(A) edges = [(A[i][j], i, j) for i in range(n) for j in range(i + 1, n) if A[i][j] > 0] edges.sort() uf = UnionFind(n) mst_edges = [] mst_weight = 0 for weight, u, v in edges: if uf.find(u) != uf.find(v): uf.union(u, v) mst_edges.append((u, v)) mst_weight += weight return mst_edges, mst_weight# 示例图的邻接矩阵A = [ [0, 10, 6, 5], [10, 0, 0, 15], [6, 0, 0, 4], [5, 15, 4, 0]]# 计算最小生成树mst_edges, mst_weight = kruskal(A)# 显示结果print('最小生成树的边:', mst_edges)print('最小生成树的权重:', mst_weight)
注意的细节知识点
初始化
确保并查集正确初始化,每个顶点的父节点为自身。边的收集与排序
收集所有边的信息,并按权值从小到大排序,以便于后续选择最小权值边。并查集查找函数
使用递归查找顶点的根节点,并进行路径压缩,提高查找效率。边的选择与合并
在选择边时,确保不会形成圈,使用并查集的查找和合并操作。更新最小生成树的边和权重,确保最小生成树的正确性。四、钢管订购和运输问题
1. 问题描述
需要从多个钢厂订购钢管,并将其运输到施工地点,铺设天然气主管道。图中表示钢厂、枢纽点和施工地点,边的权值表示运输距离。2. 问题分析
分解为两个子问题:从钢厂到枢纽点的运输和从枢纽点到施工地点的铺设。需要综合考虑订购费用、运输费用和铺设费用。
3. 模型的建立与求解
运费矩阵的计算模型:计算从供应点到需求点的最小购运费。总费用的数学规划模型:包括订购费用、运输费用和铺设管道的运费。模型求解:利用Floyd算法计算最短路径,运用数学规划模型优化总费用。MATLAB代码实例
% MATLAB代码% 构造铁路距离赋权图function dist = floyd_warshall(A) n = size(A, 1); dist = A; dist(dist == 0) = inf; dist(1:n+1:end) = 0; for k = 1:n for i = 1:n for j = 1:n if dist(i, k) + dist(k, j) < dist(i, j) dist(i, j) = dist(i, k) + dist(k, j); end end end endend% 示例图的邻接矩阵A = [0 10 inf inf inf; 10 0 5 inf inf; inf 5 0 3 inf; inf inf 3 0 1; inf inf inf 1 0];% 计算最短路径dist = floyd_warshall(A);% 计算总费用的数学规划模型function total_cost = steel_pipeline_optimization(supplies, demands, cost_matrix) n_suppliers = length(supplies); n_demands = length(demands); total_cost = 0; for i = 1:n_suppliers for j = 1:n_demands total_cost = total_cost + supplies(i) * cost_matrix(i, j); end endend% 示例数据supplies = [100, 200, 150];demands = [50, 100, 200, 100];cost_matrix = [2, 4, 5, 6; 3, 2, 7, 4; 6, 5, 2, 3];% 计算总费用total_cost = steel_pipeline_optimization(supplies, demands, cost_matrix);% 显示结果disp('总费用:');disp(total_cost);% 可视化结果figure;imagesc(dist);colorbar;title('Floyd-Warshall最短路径距离矩阵');xlabel('目标节点');ylabel('起始节点');
Floyd-Warshall算法
function dist = floyd_warshall(A)
:定义Floyd-Warshall算法函数。n = size(A, 1)
:获取图中顶点的数量。dist = A
:初始化距离矩阵。dist(dist == 0) = inf
:将邻接矩阵中的0(无边)替换为无穷大。dist(1:n+1:end) = 0
:对角线上的元素设为0,表示自身到自身的距离为0。for k = 1:n, for i = 1:n, for j = 1:n
:三重循环更新距离矩阵。if dist(i, k) + dist(k, j) < dist(i, j)
:如果通过顶点 k
的路径更短,则更新距离。dist(i, j) = dist(i, k) + dist(k, j)
:更新距离矩阵。 总费用的数学规划模型
function total_cost = steel_pipeline_optimization(supplies, demands, cost_matrix)
:定义总费用计算函数。n_suppliers = length(supplies)
:获取供应点数量。n_demands = length(demands)
:获取需求点数量。total_cost = 0
:初始化总费用。for i = 1:n_suppliers, for j = 1:n_demands
:双重循环遍历供应点和需求点。total_cost = total_cost + supplies(i) * cost_matrix(i, j)
:累加各供应点到各需求点的费用。
Python代码实例
import numpy as npdef floyd_warshall(A): n = len(A) dist = np.array(A, dtype=float) dist[dist == 0] = np.inf np.fill_diagonal(dist, 0) for k in range(n): for i in range(n): for j in range(n): if dist[i, k] + dist[k, j] < dist[i, j]: dist[i, j] = dist[i, k] + dist[k, j] return dist# 示例图的邻接矩阵A = [ [0, 10, float('inf'), float('inf'), float('inf')], [10, 0, 5, float('inf'), float('inf')], [float('inf'), 5, 0, 3, float('inf')], [float('inf'), float('inf'), 3, 0, 1], [float('inf'), float('inf'), float('inf'), 1, 0]]# 计算最短路径dist = floyd_warshall(A)def steel_pipeline_optimization(supplies, demands, cost_matrix): total_cost = 0 for i in range(len(supplies)): for j in range(len(demands)): total_cost += supplies[i] * cost_matrix[i][j] return total_cost# 示例数据supplies = [100, 200, 150]demands = [50, 100, 200, 100]cost_matrix = [ [2, 4, 5, 6], [3, 2, 7, 4], [6, 5, 2, 3]]# 计算总费用total_cost = steel_pipeline_optimization(supplies, demands, cost_matrix)# 显示结果print('总费用:', total_cost)
总费用: 7300
五、MATLAB工具箱的应用
1. MATLAB在图与网络中的应用
MATLAB提供了强大的工具箱,用于处理图与网络问题。通过编写脚本和函数,可以实现图的生成、算法的实现及结果的可视化。MATLAB代码实例
% 创建图对象并可视化G = graph([1 2 2 3 3 4 4 5 5], [2 3 4 4 5 5 1 1 2], [10 20 30 40 50 60 70 80 90]);% 可视化图figure;plot(G, 'EdgeLabel', G.Edges.Weight);title('图的可视化');% 计算最短路径[start_node, end_node] = deal(1, 5);[dist, path] = shortestpath(G, start_node, end_node);% 显示最短路径disp('最短路径:');disp(path);% 计算最小生成树T = minspantree(G);figure;plot(T, 'EdgeLabel', T.Edges.Weight);title('最小生成树');
最短路径: 80
结论
图与网络模型是解决复杂系统问题的重要工具,通过合理的算法和数学模型,可以有效地解决最短路径、最小生成树等问题。利用MATLAB和Python等工具,可以大大简化计算过程,提高工作效率。在实际应用中,图与网络模型广泛用于通信网络建设、物流运输规划等领域,具有重要的现实意义。希望这篇详细的博客总结能够帮助您理解和应用图与网络模型的基本概念、算法及其在实际问题中的应用。