运筹学——图论与最短距离(Python实现)
目录
1 概述
1.1 贪心算法
1.2 图论及求解最短距离
1.2.1 方法选择:
1.2.2 狄克斯屈拉(Dijkstra)算法
2 案例1——贪心算法实现
2.1 旅行商问题(TSP)
2.2 案例
2.3 Python实现
2.4 结果
3 案例2——图论及最短距离
3.1 知识点
3.2 networkx绘图
3.2.1 创建图
3.2.2 定点的添加、删除和查看
3.2.3 边的添加、删除和查看
3.3 案例
3.4 Python实现
3.5 结果
4 参考
1 概述
1.1 贪心算法
贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
基本思路:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到算法中的某一步不能再继续前进时,算法停止。该算法存在问题:(1)不能保证求得的最后解是最佳的;(2)不能用来求最大或最小解问题;
(3)只能求满足某些约束条件的可行解的范围。
实现该算法的伪代码:
从问题的某一初始解出发;while 能朝给定总目标前进一步 do求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;
1.2 图论及求解最短距离
1.2.1 方法选择:
(1)需要求解任意两个节点之间的最短距离,使用 Floyd 算法;
(2)只要求解单源最短路径问题,有负权边时使用 Bellman-Ford 算法,没有负权边时使用 Dijkstra 算法;
(3)A*算法找到的是相对最优路径,适合大规模、实时性高的问题。
本节我们只讨论Dijkstra 算法。
1.2.2 狄克斯屈拉(Dijkstra)算法
适用于wij≥0,给出了从vs到任意一个点vj的最短路。
Dijkstra算法是在1959年提出来的。目前公认,在所有的权wij ≥0时,这个算法是寻求最短路问题最好的算法。并且,这个算法实际上也给出了寻求从一个始定点vs到任意一个点vj的最短路。
2 案例1——贪心算法实现
2.1 旅行商问题(TSP)
旅行商问题(TravelingSalesmanProblem,TSP)一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要遍历所有城市一次且只能一次,回到出发地。应如何选择行进路线,以使总的行程最短。
旅行商问题(TSP)即给定一组城市以及每对城市之间的距离,需要找到一条最短的路线,该路线只对每个城市进行一次访问并返回起点。
这里注意汉密尔顿活路(Hamiltonian Cycle)和TSP之间的区别。汉密尔顿回路问题是要找出是否存在一次游览每个城市一次的路线。在TSP问题中,我们是已知存在汉密尔顿回路(因为该图是完整的),并且实际上,存在许多此类回路,TSP问题在于找到最小权重的汉密尔顿回路。
目前解决TSP问题的方法有许多种,比如:贪心算法、动态规划算法、分支限界法;也有智能算法。本文先介绍贪心算法:
2.2 案例
数据 如下图,第一列城市名。第二列坐标x,第三列坐标y:
贪心算法思路:随便选择出发城市,然后每次选择要去的下一个城市时,都选择还没去的最近的城市。
2.3 Python实现
#========第一步:导入相关库==================
import pandas as pd
import numpy as np
import math
import time#======第二步:读取数据=================
dataframe = pd.read_csv("旅行商问题.csv", sep=",", header=None)
v = dataframe.iloc[:, 1:3] #去除第一列12345678910,只保留x,y
print('读取数据:----------------------------')
print(v)#=======第三步:计算城市之间的距离========
train_v= np.array(v)
train_d=train_v
dist = np.zeros((train_v.shape[0],train_d.shape[0])) #初始化距离 为10*10的全0矩阵
print(dist.shape) #(10,10)#==计算距离矩阵===
for i in range(train_v.shape[0]):for j in range(train_d.shape[0]):dist[i,j] = math.sqrt(np.sum((train_v[i,:]-train_d[j,:])**2))
print('距离矩阵:----------------------------------')
print(dist)#==========第四步:计算距离和路径==============
"""
s:已经遍历过的城市
dist:城市间距离矩阵
sumpath:目前的最小路径总长度
Dtemp:当前最小距离
flag:访问标记
"""
i=1
n=train_v.shape[0]#城市个数
j=0
sumpath=0#目前的最小路径总长度
s=[]#已经遍历过的城市
s.append(0)#从城市0开始
start = time.perf_counter() #time.clock()
while True:k=1#从1开始,因为人在城市0,所以我们设定先从城市1开始选择Detemp=float('inf')#当前最小距离while True:flag=0#访问标记,否0if k in s:#是否访问,如果访问过,flag设为1flag = 1if (flag==0) and (dist[k][s[i-1]] < Detemp):#如果未访问过,并且距离小于最小距离j = k;Detemp=dist[k][s[i - 1]]; #当前两座城市相邻距离k+=1#遍历下一城市if k>=n:break;s.append(j)i+=1;sumpath+=Detempif i>=n:break;sumpath+=dist[0][j]#加上dist[0][j] 表示最后又回到起点
end = time.perf_counter() #time.clock()
print("距离:")
print(sumpath)
print('*--------------*')
print('路径:')
for m in range(n):print("%s-> "%(s[m]),end='')
print()
print("程序的运行时间是:%s"%(end-start))
代码解析:数字k表示当前我们选择前往下一个城市时,我们需要计算所有未访问过的城市和当前城市距离。
数字i 用于控制访问过的城市,我们需要到达每一个城市。代码中有两个while
里面那个while表示选择下一城市时,需要遍历所有未访问过的城市,然后选择距离当前城市最近的城市,赋值给j
外面while,表示我们的每一步,我们需要去每个城市。
2.4 结果
读取数据:----------------------------1 2
0 2066 2333
1 935 1304
2 1270 200
3 1389 700
4 984 2810
5 2253 478
6 949 3025
7 87 2483
8 3094 1883
9 2706 3130
(10, 10)
距离矩阵:----------------------------------
[[ 0. 1529.05264788 2276.68728639 1767.77204413 1182.477483931864.40178073 1313.98363765 1984.67654795 1122.17823896 1022.15898959][1529.05264788 0. 1153.70750193 755.6004235 1506.79693391555.44205935 1721.0569427 1452.28957168 2235.29013777 2543.76040538][2276.68728639 1153.70750193 0. 513.96595218 2625.62297371021.55420806 2843.17885473 2571.29889356 2481.82694804 3262.97349055][1767.77204413 755.6004235 513.96595218 0. 2148.51693035892.06502005 2366.26815894 2207.7801068 2075.21420581 2763.94446399][1182.47748393 1506.7969339 2625.6229737 2148.51693035 0.2654.91713618 217.83020911 954.74499213 2304.65377009 1751.48051659][1864.40178073 1555.44205935 1021.55420806 892.06502005 2654.917136180. 2861.40262808 2951.53875123 1637.46938903 2690.41130685][1313.98363765 1721.0569427 2843.17885473 2366.26815894 217.830209112861.40262808 0. 1018.23769327 2430.05946429 1760.13465394][1984.67654795 1452.28957168 2571.29889356 2207.7801068 954.744992132951.53875123 1018.23769327 0. 3066.2760802 2697.7342345 ][1122.17823896 2235.29013777 2481.82694804 2075.21420581 2304.653770091637.46938903 2430.05946429 3066.2760802 0. 1305.9682232 ][1022.15898959 2543.76040538 3262.97349055 2763.94446399 1751.480516592690.41130685 1760.13465394 2697.7342345 1305.9682232 0. ]]
距离:
10464.183486532447
*--------------*
路径:
0-> 9-> 8-> 5-> 3-> 2-> 1-> 7-> 4-> 6->
程序的运行时间是:0.0002605780000024538Process finished with exit code 0
3 案例2——图论及最短距离
3.1 知识点
3.2 networkx绘图
3.2.1 创建图
networkx有四种图 Graph 、DiGraph、MultiGraph、MultiDiGraph,分别为无多重边无向图、无多重边有向图、有多重边无向图、有多重边有向图。
#==========创建图================import networkx as nx # 导入 NetworkX 工具包 G1 = nx.Graph() # 创建:空的 无向图 G2 = nx.DiGraph() #创建:空的 有向图 G3 = nx.MultiGraph() #创建:空的 多图 G4 = nx.MultiDiGraph() #创建:空的 有向多图
3.2.2 定点的添加、删除和查看
#================顶点的添加、删除和查看=============#======顶点(node)的操作=====#==向图中添加顶点== G1.add_node(1) # 向 G1 添加顶点 1 G1.add_node(1, name='n1', weight=1.0) # 添加顶点 1,定义 name, weight 属性 G1.add_node(2, date='May-16') # 添加顶点 2,定义 time 属性 G1.add_nodes_from([3, 0, 6], dist=1) # 添加多个顶点,并定义属性 G1.add_nodes_from(range(10, 15)) # 向图 G1 添加顶点 10~14#==查看顶点和顶点属性== print(G1.nodes()) # 查看顶点列表 # [1, 2, 3, 0, 6, 10, 11, 12, 13, 14] print(G1._node) # 查看顶点属性 # {1: {'name': 'n1', 'weight': 1.0}, 2: {'date': 'May-16'}, 3: {'dist': 1}, 0: {'dist': 1}, 6: {'dist': 1}, 10: {}, 11: {}, 12: {}, 13: {}, 14: {}}#==从图中删除顶点== G1.remove_node(1) # 删除顶点 G1.remove_nodes_from([1, 11, 13, 14]) # 通过顶点标签的 list 删除多个顶点 print(G1.nodes()) # 查看顶点 # [2, 3, 0, 6, 10, 12] # 顶点列表
3.2.3 边的添加、删除和查看
#====================边的添加、删除和查看================#========边(edge)的操作========#==向图中添加边== G1.add_edge(1,5) # 向 G1 添加边,并自动添加图中没有的顶点 G1.add_edge(0,10, weight=2.7) # 向 G1 添加边,并设置边的属性 G1.add_edges_from([(1,2,{'weight':0}), (2,3,{'color':'blue'})]) # 向图中添加边,并设置#==属性== G1.add_edges_from([(3,6),(1,2),(6,7),(5,10),(0,1)]) # 向图中添加多条边 G1.add_weighted_edges_from([(1,2,3.6),[6,12,0.5]]) # 向图中添加多条赋权边: (node1,node2,weight) print(G1.nodes()) # 查看顶点 # [2, 3, 0, 6, 10, 12, 1, 5, 7] # 自动添加了图中没有的顶点#==从图中删除边== G1.remove_edge(0,1) # 从图中删除边 0-1 G1.remove_edges_from([(2,3),(1,5),(6,7)]) # 从图中删除多条边#==查看边和边的属性== print(G1.edges) # 查看所有的边 [(2, 1), (3, 6), (0, 10), (6, 12), (10, 5)] print(G1.get_edge_data(1,2)) # 查看指定边的属性 # {'weight': 3.6} print(G1[1][2]) # 查看指定边的属性 # {'weight': 3.6} print(G1.edges(data=True)) # 查看所有边的属性 # [(2, 1, {'weight': 3.6}), (3, 6, {}), (0, 10, {'weight': 2.7}), (6, 12, {'weight': 0.5}), (10, 5, {})]
3.3 案例
例题 1:已知如图的有权无向图,求顶点 v1 到 顶点 v11 的最短路径。
3.4 Python实现
#========导入相关包=========================
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx # 导入 NetworkX 工具包#======问题:无向图的最短路问题===============
G1 = nx.Graph() # 创建:空的 无向图
G1.add_weighted_edges_from([(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)]) # 向图中添加多条赋权边: (node1,node2,weight)
print('nx.info:',G1.nodes) # 返回图的基本信息,nx.info:返回图的基本信息#=======两个指定顶点之间的最短加权路径===============
minWPath_v1_v11 = nx.dijkstra_path(G1, source=1, target=11) # 顶点 1 到 顶点 11 的最短加权路径
print("顶点 v1 到 顶点 v11 的最短加权路径: ", minWPath_v1_v11)
# 两个指定顶点之间的最短加权路径的长度
lMinWPath_v1_v11 = nx.dijkstra_path_length(G1, source=1, target=11) # 最短加权路径长度
print("顶点 v1 到 顶点 v11 的最短加权路径长度: ", lMinWPath_v1_v11)pos = {1: (0,4), 2: (5,7), 3: (5,4), 4: (5,1), 5: (10,7), 6: (10,4), 7: (10,1),8: (15,7), 9: (15,4), 10: (15,1), 11: (20,4)} # 指定顶点位置,以节点为键,位置为值的字典
labels = nx.get_edge_attributes(G1, 'weight') # 设置边的 labels 为 ‘weight'
nx.draw(G1, pos,node_color = 'r',with_labels=True, font_color='b') # 绘制无向图,pos 指的是布局
nx.draw_networkx_edge_labels(G1, pos, edge_labels=labels, font_color='y') # 显示边的权值
plt.show()
1、Python Network(二)绘图draw系列draw(),draw_networkx(),draw_networkx_nodes(),draw_networkx_edges()
2、用Python的networkx绘制精美网络图
3.5 结果
nx.info: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
顶点 v1 到 顶点 v11 的最短加权路径: [1, 2, 5, 6, 3, 7, 10, 9, 11]
顶点 v1 到 顶点 v11 的最短加权路径长度: 13
4 参考
————————————————
https://blog.csdn.net/youcans/article/details/118555468
运筹学——图论与最短距离(Python实现)相关推荐
- 王道论坛机考系列——图论之最短距离
图论 王道论坛机考系列--图论之最短距离 最短距离--Floyd算法(解决全源) 最短路--Floyd算法 最短路--Dijkstra算法一 最短路径问题--Dijkstra算法二 王道论坛机考系列- ...
- python两点之间最短距离_寻找两点之间的最短距离Python
我有两个数据框.一个包含properties locations,另一个包含railway stations locations. 属性数据框样本(原始数据框由约700行组成): properties ...
- 一文带你入门图论和网络分析(附Python代码)
作者:Srivatsa 翻译:和中华 校对:丁楠雅 本文约6300字,建议阅读20+分钟. 本文从图的概念以及历史讲起,并介绍了一些必备的术语,随后引入了networkx库,并以一个航班信息数据集为例 ...
- 运筹学研究者关注的Github和CSDN账号
1 Sudhan Bhattarai 首页链接:https://github.com/sudhan-bhattarai 列表: CVRPTW模型及验证: Multi-period Home Heal ...
- 2020年全国大学生数学建模竞赛赛题 选题建议
A题 每年都有的,典型的物理学问题.题目中可能要用到:运筹学,物理学,仿真模拟的相关知识.我记得去年还是前年也有过关于温度的题目,大家可以看看是否有可借鉴的地方.此题如果不是相关专业的同学,或者编程能 ...
- 关于写论文的资料总结
文章目录 一.常用查找期刊会议的网站 二.使用过的matlab.mininet等仿真工具 三.拓扑 四.Python 五.图论与运筹学 六.研究过的一些元启发式算法 七.如何发论文? 八.看过和用过的 ...
- 计算机都学什么数学,计算机专业的数学应学到什么水平?应该学习数学的那些分支?...
本人是数学系的, 但其实真正的爱好是计算机. 当年报志愿的时候不知听谁说了句, 学计算机, 想搞软件就去学数学, 想搞硬件就去学物理, 然后就报了数学. 在数学系, 感觉思维确实受到了训练, 但所学的 ...
- 用MATLAB做周期三角波的傅里叶级数,Matlab 周期方波信号傅里叶级数展开
方波信号为: 傅里叶级数展开为: 程序运行结果: 程序代码: clear x = -6:0.01:6; T = 4; f = x; for N = 1:length(f) temp = rem(abs ...
- 计算机专业的数学应学到什么水平?应该学习数学的那些分支?
本人是数学系的, 但其实真正的爱好是计算机. 当年报志愿的时候不知听谁说了句, 学计算机, 想搞软件就去学数学, 想搞硬件就去学物理, 然后就报了数学. 在数学系, 感觉思维确实受到了训练, 但所学的 ...
最新文章
- html 标签、图像、链接、注释、锚点定位、特殊字符
- python 面向对象学习
- Google BERT模型提取句子Token特征
- Boost:bind绑定的回归测试
- 分布式ID-数据库多主模式
- Netflix:如何通过机器学习提高流媒体质量?
- leetcode 659. 分割数组为连续子序列(贪心算法)
- ad电阻原理图_负载电阻的原理及应用
- QT QTransform与QMatrix 有啥区别?
- Thrift 教程 开发 笔记 原理 资料 使用 范例 示例 应用
- 解决:虚拟机能ping通主机,主机ping不通虚拟机
- MTK 驱动(38)---MTK 待机问题分析
- python槽怎么用_【Python成长之路】从零学GUI -- 多窗口跳转(信号与槽函数用法)...
- mondrian olap 示例_详解OLAP概念、基本内容、特点、分类
- python与机器学习(四)——线性回归和逻辑斯蒂回归
- Redis开发运维实践上线部署规划之持久化设置
- 是否有一个“先前的兄弟”选择器?
- VS2017下载地址和安装教程(图解)
- 安装CommonAPI
- mysql distribute by_Mysql与分布式Gaussdb 100语法差异及常见问题汇总