作者:欧新宇(Xinyu OU)
当前版本:Release v1.0
开发平台:Python3.11
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce GTX 1080 Ti
本教案为非完整版教案,请结合课程PPT使用。
最后更新:2024年4月11日
# Lec0701_A_NetworkX_Example
import networkx as nx
import matplotlib.pyplot as plt
# 1. 绘制一个三正则图
# 1.1 构造三正则图
plt.figure(figsize=(12, 4)) # 定义画板的尺度
G = nx.cubical_graph() # 生成一个3正则图
# 2. 使用NetworkX绘图
plt.subplot(121) # 激活1号子窗口
nx.draw(G, with_labels=True) # 绘制图G,并附上图的标号
# 2. 为三正则图添加一些属性
plt.subplot(122)
s = ['$v_' + str(i) + '$' for i in range(8)] # 添加顶点描述
s = dict(zip(range(8), s)) # 构造顶点标注的字符字典{ID: Desc}
nx.draw(G, labels=s, node_color='y', node_shape='s', edge_color='b') # 添加属性
plt.show()
# Lec0702_AddNodeEdge
import networkx as nx
import matplotlib.pyplot as plt
# 1. 定义图G的顶点和边
G = nx.Graph()
G.add_node('x') # 添加编号为x的一个顶点
G.add_nodes_from([1, 'B']) # 从列表中添加多个顶点
G.add_edge(1, 'B') # 添加顶点1和B之间的一条边
G.add_edge('x', 1, weight=0.5) # 添加顶点x和1之间权重为0.5的一条边
e = [(1,'B',0.3), ('B','C',0.9), (1,'C',0.5), ('C',3,1.2)] # 批量定义赋权边
G.add_weighted_edges_from(e) # 从列表中添加多条赋权边
# 2. 输出图G
print(G.adj) # 显示图的邻接表的字典数据
plt.figure(figsize=(4, 3)) # 使用plt库定义图的尺度(可省略)
nx.draw(G, with_labels=True, node_color='pink') # 绘制带顶点标签的图G
plt.show()
{'x': {1: {'weight': 0.5}},
1: {'B': {'weight': 0.3}, 'x': {'weight': 0.5}, 'C': {'weight': 0.5}},
'B': {1: {'weight': 0.3}, 'C': {'weight': 0.9}},
'C': {'B': {'weight': 0.9}, 1: {'weight': 0.5}, 3: {'weight': 1.2}},
3: {'C': {'weight': 1.2}}}
# Lec0703_DiGraph
import networkx as nx
import matplotlib.pyplot as plt
# 1. 定义图G的顶点和边
G = nx.DiGraph() # 初始化有向图
G.add_nodes_from(range(1, 7)) # 构造顶点标
List = [(1,2),(1,3),(2,3),(3,2),(3,5),(4,2),(4,6),(5,2),(5,4),(6,5),(1,6)] # 定义边序列
G.add_edges_from(List) # 构造有向边
# 2. 输出图
# 2.1 绘制图
pos = nx.shell_layout(G) # 顶点在多个同心圆上分布
plt.figure(figsize=(4, 3)) # 使用plt库定义图的尺度(可省略)
nx.draw(G, pos, with_labels=True, font_weight='bold', node_color='y') # 定义图的样式
plt.show()
# 2.2 输出图的邻接矩阵
print(G.adj) # 输出图G的邻接矩阵字典
W = nx.to_numpy_array(G) # 输出图G的邻接矩阵
print('AdjacencyMatrix = \n {}'.format(W));
{1: {2: {}, 3: {}, 6: {}},
2: {3: {}},
3: {2: {},
5: {}},
4: {2: {}, 6: {}}, 5: {2: {}, 4: {}},
6: {5: {}}}
AdjacencyMatrix =
[[0. 1. 1. 0. 0. 1.]
[0. 0. 1. 0. 0. 0.]
[0. 1. 0. 0. 1. 0.]
[0. 1. 0. 0. 0. 1.]
[0. 1. 0. 1. 0. 0.]
[0. 0. 0. 0. 1. 0.]]
# Lec0704_WeightedGraph
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(4, 3)) # 使用plt库定义图的尺度(可省略)
G = nx.Graph()
List=[(1, 3, 10), (1, 4, 60), (2, 3, 5), (2, 4, 20), (3, 4, 1)]
G.add_nodes_from(range(1,5))
G.add_weighted_edges_from(List)
W1 = nx.to_numpy_array(G) # 从图G导出权重邻接矩阵
W2 = nx.get_edge_attributes(G, 'weight') # 导出赋权边的字典数据
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, font_weight='bold')
nx.draw_networkx_edge_labels(G, pos, font_size=13, edge_labels=W2)
print('邻接矩阵为:\n', W1)
print('邻接表字典为:\n', G.adj)
print('邻接表列表为:\n', list(G.adjacency()))
print('列表字典为:\n', nx.to_dict_of_lists(G))
np.savetxt('../../Data/Lec07/data0804.txt', W1, fmt='%d') # 邻接矩阵保存到文本文件
plt.show()
邻接矩阵为:
[[ 0. 0. 10. 60.]
[ 0. 0. 5. 20.]
[10. 5. 0. 1.]
[60. 20. 1. 0.]]
邻接表字典为:
{1: {3: {'weight': 10}, 4: {'weight': 60}}, 2: {3: {'weight': 5}, 4: {'weight': 20}}, 3: {1: {'weight': 10}, 2: {'weight': 5}, 4: {'weight': 1}}, 4: {1: {'weight': 60}, 2: {'weight': 20}, 3: {'weight': 1}}}
邻接表列表为:
[(1, {3: {'weight': 10}, 4: {'weight': 60}}), (2, {3: {'weight': 5}, 4: {'weight': 20}}), (3, {1: {'weight': 10}, 2: {'weight': 5}, 4: {'weight': 1}}), (4, {1: {'weight': 60}, 2: {'weight': 20}, 3: {'weight': 1}})]
列表字典为:
{1: [3, 4], 2: [3, 4], 3: [1, 2, 4], 4: [1, 2, 3]}
# Lec0705_Dijkstra_MinimumTravelFee0607
import networkx as nx
G = nx.DiGraph()
List = [(1,2,6), (1,3,3), (1,4,1), (2,5,1), (3,2,2), (3,4,2), (4,6,10), (5,4,6),
(5,6,4), (5,7,3), (5,8,6), (6,5,10), (6,7,2), (7,8,4), (9,5,2), (9,8,3)]
G.add_nodes_from(range(1,10))
G.add_weighted_edges_from(List)
path = nx.dijkstra_path(G, 1, 8, weight='weight') # 求最短路径
d = nx.dijkstra_path_length(G, 1, 8, weight='weight') # 求最短路径费用(权重)
print('最短路径为:', path)
print('最小费用为:', d)
最短路径为: [1, 3, 2, 5, 8]
最小费用为: 12
# Lec0706_Flord0608
import networkx as nx
import numpy as np
G = nx.Graph()
List = [(1, 3, 10), (1, 4, 60), (2, 3, 5), (2, 4, 20), (3, 4, 1)]
G.add_nodes_from(range(1,5))
G.add_weighted_edges_from(List)
d = nx.floyd_warshall_numpy(G)
print('最短距离矩阵为:\n', d)
path = nx.shortest_path(G, weight='weight', method='bellman-ford')
for i in range(1,len(d)):
for j in range(i+1, len(d)+1):
print('顶点{}到顶点{}的最短路径为:'.format(i,j), path[i][j])
最短距离矩阵为:
[[ 0. 15. 10. 11.]
[15. 0. 5. 6.]
[10. 5. 0. 1.]
[11. 6. 1. 0.]]
顶点1到顶点2的最短路径为: [1, 3, 2]
顶点1到顶点3的最短路径为: [1, 3]
顶点1到顶点4的最短路径为: [1, 3, 4]
顶点2到顶点3的最短路径为: [2, 3]
顶点2到顶点4的最短路径为: [2, 3, 4]
顶点3到顶点4的最短路径为: [3, 4]
# Lec0707_DeviceUpdate0609
import numpy as np
import networkx as nx
import pylab as plt
a=np.zeros((6,6))
a[0,1:]=[15,20,27,37,54]
a[1,2:]=[15,20,27,37]; a[2,3:]=[16,21,28];
a[3,4:]=[16,21]; a[4,5]=17
G = nx.DiGraph(a) #构造赋权有向图,顶点编号为0,1,…,5
p = nx.shortest_path(G,0,5,weight='weight')
d = nx.shortest_path_length(G,0,5,weight='weight')
print('path=',p); print('d=',d)
plt.rc('font',size=16)
pos = nx.shell_layout(G) #设置布局
w = nx.get_edge_attributes(G,'weight')
key = range(6)
s = ['v'+str(i+1) for i in key]
s = dict(zip(key,s)) #构造用于顶点标注的字符字典
nx.draw(G, pos, font_weight='bold', labels=s, node_color='r')
nx.draw_networkx_edge_labels(G, pos, edge_labels=w)
path_edges = list(zip(p, p[1:]))
nx.draw_networkx_edges(G, pos, edgelist=path_edges,
edge_color='r', width=3)
plt.savefig('../../Data/Lec07/fig6_9.png', dpi=500)
plt.show()
path= [0, 2, 5]
d= 48.0
# Lec0708_AddressSelection0610
import numpy as np
import networkx as nx
L=[(1,2,20), (1,5,15), (2,3,20), (2,4,60), (2,5,25), (3,4,30), (3,5,18), (5,6,15)]
G = nx.Graph(); G.add_nodes_from(np.arange(1,7))
G.add_weighted_edges_from(L)
d = nx.floyd_warshall_numpy(G)
d = np.array(d) #转换为array数组
md = np.max(d, axis=1) #逐行求最大值
mmd = min(md) #求最小值
ind = np.argmin(md)+1 #求最小值的地址
print(d); print("最小值为:", mmd)
print("最小值的地址为:", ind)
[[ 0. 20. 33. 63. 15. 30.]
[20. 0. 20. 50. 25. 40.]
[33. 20. 0. 30. 18. 33.]
[63. 50. 30. 0. 48. 63.]
[15. 25. 18. 48. 0. 15.]
[30. 40. 33. 63. 15. 0.]]
最小值为: 33.0
最小值的地址为: 3
# Lec0709_SpanningTree0612
import networkx as nx
import pylab as plt
L = [(0,1,2),(0,2,1),(0,3,3),(0,4,4),(0,5,4),(0,6,2),(0,7,5),(0,8,4),(1,2,4),(1,8,1),(2,3,1),(3,4,1),(4,5,5),(5,6,2),(6,7,3),(7,8,5)]
G = nx.Graph()
G.add_weighted_edges_from(L)
T = nx.minimum_spanning_tree(G) # 返回可迭代对象
c = nx.to_numpy_array(T) # 返回最小生成树的邻接矩阵
print("邻接矩阵 AdjMatrix = \n",c)
w = c.sum()/2 #求最小生成树的权重
print("最小生成树的权重 W =",w)
plt.figure(figsize=(10, 4))
pos = nx.circular_layout(G)
plt.subplot(121) # 绘制连通图
plt.title('Connected Graph', fontsize=12)
nx.draw(G,pos)
w1 = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=w1)
plt.subplot(122) #绘制最小生成树
plt.title('Minimum Spanning Tree', fontsize=12)
nx.draw(T, pos, with_labels=True, font_weight='bold')
w2 = nx.get_edge_attributes(T, 'weight')
nx.draw_networkx_edge_labels(T, pos, edge_labels=w2)
plt.subplots_adjust(wspace=0.5)
plt.show()
邻接矩阵 AdjMatrix =
[[0. 2. 1. 0. 0. 0. 2. 0. 0.]
[2. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 1. 0. 1. 0. 0. 0. 0.]
[0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 2. 0. 0.]
[2. 0. 0. 0. 0. 2. 0. 3. 0.]
[0. 0. 0. 0. 0. 0. 3. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0.]]
最小生成树的权重 W = 13.0
# Lec0710_MaximumFlow0617
import numpy as np
import networkx as nx
import pylab as plt
L = [(1,2,6),(1,3,4),(1,4,5),(2,3,3),(2,5,9),(2,6,9),
(3,4,5),(3,5,6),(3,6,7),(3,7,3),(4,3,2),(4,7,5),
(5,8,12),(6,5,8),(6,8,10),(7,6,4),(7,8,15)]
G = nx.DiGraph()
G.add_nodes_from(range(1,9))
G.add_weighted_edges_from(L,weight='capacity')
value, flow_dict = nx.maximum_flow(G, 1, 8)
print("最大流的流量为:",value)
print("最大流为:", flow_dict)
n = len(flow_dict)
adj_mat = np.zeros((n, n), dtype=int)
for i, adj in flow_dict.items():
for j, weight in adj.items():
adj_mat[i-1,j-1] = weight
print("最大流的邻接矩阵为:\n",adj_mat)
ni, nj = np.nonzero(adj_mat) #非零弧的两端点编号
plt.rc('font', size=16)
pos = nx.shell_layout(G) #设置布局
w = nx.get_edge_attributes(G,'capacity')
nx.draw(G,pos,font_weight='bold', with_labels=True, node_color='y')
nx.draw_networkx_edge_labels(G, pos, edge_labels=w)
path_edges = list(zip(ni+1, nj+1))
nx.draw_networkx_edges(G,pos, edgelist=path_edges, edge_color='r', width=3)
plt.show()
最大流的流量为: 15
最大流为:
{1: {2: 6, 3: 4, 4: 5},
2: {3: 0, 5: 6, 6: 0},
3: {4: 0, 5: 4, 6: 0, 7: 0},
4: {3: 0, 7: 5},
5: {8: 10},
6: {5: 0, 8: 0},
7: {6: 0, 8: 5},
8: {}}
最大流的邻接矩阵为:
[[ 0 6 4 5 0 0 0 0]
[ 0 0 0 0 6 0 0 0]
[ 0 0 0 0 4 0 0 0]
[ 0 0 0 0 0 0 5 0]
[ 0 0 0 0 0 0 0 10]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 5]
[ 0 0 0 0 0 0 0 0]]
# Lec0711 EmploymentMaximumFlow0618
import numpy as np
import networkx as nx
node=['s','A','B','C','D','E','2b','3b','4b','1','2','3','4','t']
n = len(node); G = nx.DiGraph(); G.add_nodes_from(node)
L = [('s','A',4),('s','B',3),('s','C',3),('s','D',2),('s','E',4),('A','2b',1),
('A','1',1),('A','3b',1),('A','4b',1),('B','2b',1),('B','1',1),('B','3',1),
('B','4b',1),('C','1',1),('C','2',1),('C','3',1),('C','4b',1),('D','1',1),
('D','2',1),('D','3b',1),('D','4b',1),('E','2b',1),('E','1',1),('E','3',1),
('E','4',1),('2b','2',2),('3b','3',1),('4b','4',2),('1','t',5),('2','t',4),
('3','t',4),('4','t',3)]
for k in range(len(L)):
G.add_edge(L[k][0], L[k][1], capacity=L[k][2])
value, flow_dict = nx.maximum_flow(G, 's', 't')
print(value);
print(flow_dict)
A = np.zeros((n,n),dtype=int)
for i, adj in flow_dict.items():
for j, f in adj.items():
A[node.index(i), node.index(j)] = f
print(A)
16
{'s': {'A': 4, 'B': 3, 'C': 3, 'D': 2, 'E': 4}, 'A': {'2b': 1, '1': 1, '3b': 1, '4b': 1}, 'B': {'2b': 0, '1': 1, '3': 1, '4b': 1}, 'C': {'1': 1, '2': 1, '3': 1, '4b': 0}, 'D': {'1': 1, '2': 1, '3b': 0, '4b': 0}, 'E': {'2b': 1, '1': 1, '3': 1, '4': 1}, '2b': {'2': 2}, '3b': {'3': 1}, '4b': {'4': 2}, '1': {'t': 5}, '2': {'t': 4}, '3': {'t': 4}, '4': {'t': 3}, 't': {}}
[[0 4 3 3 2 4 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 1 1 1 0 0 0 0]
[0 0 0 0 0 0 0 0 1 1 0 1 0 0]
[0 0 0 0 0 0 0 0 0 1 1 1 0 0]
[0 0 0 0 0 0 0 0 0 1 1 0 0 0]
[0 0 0 0 0 0 1 0 0 1 0 1 1 0]
[0 0 0 0 0 0 0 0 0 0 2 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 2 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 5]
[0 0 0 0 0 0 0 0 0 0 0 0 0 4]
[0 0 0 0 0 0 0 0 0 0 0 0 0 4]
[0 0 0 0 0 0 0 0 0 0 0 0 0 3]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
# Lec0711-2 EmploymentMaximumFlow0618-2
import numpy as np
import cvxpy as cp
a = np.array([4, 3, 3, 2, 4])
x = cp.Variable((4,5), integer=True)
obj = cp.Maximize(cp.sum(x))
con = [cp.sum(x, axis=0) <= a,
x[1,0]+x[1,1]+x[1,4] <= 2,
x[2,0]+x[2,3] <= 1,
sum(x[3,:-1]) <= 2, x>=0, x<=1]
prob = cp.Problem(obj, con)
prob.solve(solver='GLPK_MI')
print("最优值为:", prob.value)
print("最优解为:\n", x.value)
最优值为: 16.0
最优解为:
[[1. 1. 1. 1. 1.]
[1. 0. 1. 1. 1.]
[1. 1. 1. 0. 1.]
[1. 1. 0. 0. 1.]]
# Lec0712-3 FeeMaximnumFlow0619
import numpy as np
import networkx as nx
L = [('vs','v2',5,3),('vs','v3',3,6),('v2','v4',2,8),('v3','v2',1,2),('v3','v5',4,2),
('v4','v3',1,1),('v4','v5',3,4),('v4','vt',2,10),('v5','vt',5,2)]
G = nx.DiGraph()
for k in range(len(L)):
G.add_edge(L[k][0], L[k][1], capacity=L[k][2], weight=L[k][3])
maxFlow = nx.max_flow_min_cost(G,'vs','vt')
print("所求最大流为:", maxFlow)
mincost = nx.cost_of_flow(G, maxFlow)
print("最小费用为:", mincost)
node = list(G.nodes()) # 导出顶点列表
n = len(node); flow_mat = np.zeros((n,n))
for i,adj in maxFlow.items():
for j,f in adj.items():
flow_mat[node.index(i), node.index(j)] = f
print("最大流的流量为:", sum(flow_mat[:,-1]))
print("最小费用最大流的邻接矩阵为:\n", flow_mat)
所求最大流为: {'vs': {'v2': 2, 'v3': 3}, 'v2': {'v4': 2}, 'v3': {'v2': 0, 'v5': 4}, 'v4': {'v3': 1, 'v5': 1, 'vt': 0}, 'v5': {'vt': 5}, 'vt': {}}
最小费用为: 63
最大流的流量为: 5.0
最小费用最大流的邻接矩阵为:
[[0. 2. 3. 0. 0. 0.]
[0. 0. 0. 2. 0. 0.]
[0. 0. 0. 0. 4. 0.]
[0. 0. 1. 0. 1. 0.]
[0. 0. 0. 0. 0. 5.]
[0. 0. 0. 0. 0. 0.]]
某项目工程由11项作业组成(分别用代号表示),其计划完成时间及活动间相互关系如下表所示,求活动的关键路径。
# Lec0713: CriticalPath0620
import cvxpy as cp
x = cp.Variable(8, pos=True)
L = [(1,2,5), (1,3,10), (1,4,11), (2,5,4), (3,4,4), (3,5,0), (4,6,15),
(5,6,21), (5,7,25), (5,8,35), (6,7,0), (6,8,20), (7,8,15)]
obj = cp.Minimize(sum(x));
con = []
for k in range(len(L)):
con.append(x[L[k][1]-1] >= x[L[k][0]-1] + L[k][2])
prob = cp.Problem(obj, con)
prob.solve(solver='GLPK_MI')
print('最优值为:', prob.value);
print('最优解为:', x.value)
最优值为: 156.0
最优解为: [ 0. 5. 10. 14. 10. 31. 35. 51.]
求例6.20中每个作业的最早开工时间、最迟开工时间和作业的关键路径。
# Lec0714: CriticalPath0621
import numpy as np
import cvxpy as cp
n =8; x = cp.Variable(n, pos=True); z = np.zeros(n)
L = [(1,2,5), (1,3,10), (1,4,11), (2,5,4), (3,4,4), (3,5,0), (4,6,15),
(5,6,21), (5,7,25), (5,8,35), (6,7,0), (6,8,20), (7,8,15)]
obj = cp.Minimize(sum(x)); con = []
for k in range(len(L)):
con.append(x[L[k][1]-1] >= x[L[k][0]-1] + L[k][2])
prob = cp.Problem(obj, con); prob.solve(solver='GLPK_MI')
print('最优值为', prob.value); print('最优解为:', x.value)
xx = x.value; z[-1] = xx[-1]
for k in range(n-1, 0, -1):
z[k-1]=min([z[a[1]-1]-a[2] for a in L if a[0]==k])
print('z:', z); es=[]; lf=[]; ls=[]; ef=[]
for i in range(len(L)):
es.append([L[i][0], L[i][1], xx[L[i][0]-1]])
lf.append([L[i][0], L[i][1], z[L[i][1]-1]])
ls.append([L[i][0], L[i][1], z[L[i][1]-1]-L[i][2]])
ef.append([L[i][0], L[i][1], xx[L[i][0]-1]+L[i][2]])
print('作业最早开工时间如下:'); print(es)
print('作业最晚开工时间如下:'); print(ls)
最优值为 156.0
最优解为: [ 0. 5. 10. 14. 10. 31. 35. 51.]
z: [ 0. 6. 10. 16. 10. 31. 36. 51.]
作业最早开工时间如下:
[[1, 2, 0.0], [1, 3, 0.0], [1, 4, 0.0], [2, 5, 5.0], [3, 4, 10.0], [3, 5, 10.0], [4, 6, 14.0], [5, 6, 10.0], [5, 7, 10.0], [5, 8, 10.0], [6, 7, 31.0], [6, 8, 31.0], [7, 8, 35.0]]
作业最晚开工时间如下:
[[1, 2, 1.0], [1, 3, 0.0], [1, 4, 5.0], [2, 5, 6.0], [3, 4, 12.0], [3, 5, 10.0], [4, 6, 16.0], [5, 6, 10.0], [5, 7, 11.0], [5, 8, 16.0], [6, 7, 36.0], [6, 8, 31.0], [7, 8, 36.0]]
# Lec0715: CriticalPath0622
import numpy as np
import cvxpy as cp
L = [(1,2,5), (1,3,10), (1,4,11), (2,5,4), (3,4,4), (3,5,0), (4,6,15),
(5,6,21), (5,7,25), (5,8,35), (6,7,0), (6,8,20), (7,8,15)]
x = cp.Variable((8,8), integer=True); fun = 0
for i in range(len(L)):
fun = fun + x[L[i][0]-1,L[i][1]-1] * L[i][2]
obj = cp.Maximize(fun); con =[ x>=0, x<=1]
out = [a[1]-1 for a in L if a[0]==1] #起点相邻顶点的编号
con.append(sum(x[0,out])==1) #起点发出弧的约束
ind = [a[0]-1 for a in L if a[1]==8] #终点相邻顶点的编号
con.append(sum(x[ind,7])==1) #终点进入弧的约束
for k in range(2,8):
out = [a[1]-1 for a in L if a[0]==k] #k的出弧的相邻顶点
ind = [a[0]-1 for a in L if a[1]==k] #k的入弧的相邻顶点
con.append(sum(x[k-1,out])==sum(x[ind,k-1]))
prob = cp.Problem(obj, con); prob.solve(solver='GLPK_MI')
print('最优值为', prob.value); print('最优解为:\n', x.value)
ni, nj = np.nonzero(x.value)
print('关键路径的端点为:'); print(ni+1); print(nj+1)
最优值为 51.0
最优解为:
[[0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
关键路径的端点为:
[1 3 5 6]
[3 5 6 8]
假设例6.20中所列的工程要求在49天内完成。为提前完成工程,有些作业需要加快进度,缩短工期,而加快进度需要额外增加费用。表6.6列出例6.20中可缩短工期的所有作业和缩短一天工期额外增加的费用。现在的问题是,如何安排作业才能使额外增加的总费用最少。
# Lec0716: CriticalPath0623
import numpy as np
import cvxpy as cp
L = [(1,2,5,5,0), (1,3,10,8,700), (1,4,11,8,400), (2,5,4,3,450), (3,4,4,4,0),
(3,5,0,0,0), (4,6,15,15,0), (5,6,21,16,600), (5,7,25,22,300),
(5,8,35,30,500), (6,7,0,0,0), (6,8,20,16,500), (7,8,15,12,400)]
x = cp.Variable(8, pos=True)
y = cp.Variable((8,8), integer=True); fun = 0
for i in range(len(L)):
fun = fun + y[L[i][0]-1,L[i][1]-1] * L[i][4]
obj = cp.Minimize(fun); con =[x[7]-x[0]<=49, y>=0]
for i in range(len(L)):
con.append(x[L[i][1]-1]-x[L[i][0]-1]+y[L[i][0]-1,L[i][1]-1]>=L[i][2])
con.append(y[L[i][0]-1,L[i][1]-1]<=L[i][2]-L[i][3])
prob = cp.Problem(obj, con); prob.solve(solver='GLPK_MI')
print('最优值为', prob.value); print('x的取值为:\n', x.value)
print('y的取值为:\n', y.value); ni, nj = np.nonzero(y.value)
print('压缩工期的作业为:'); print(ni+1); print(nj+1)
最优值为 1200.0
x的取值为:
[ 0. 5. 9. 13. 9. 30. 34. 49.]
y的取值为:
[[0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
压缩工期的作业为:
[1 6]
[3 8]
用Python求解例6.23,并求出相应的关键路径、各作业的最早开工时间和最迟开工时间。(如需要知道压缩工期后的关键路径,则需要稍复杂一点的计算。)
# Lec0717: CriticalPath0624
import numpy as np
import cvxpy as cp
L = [(1,2,5,5,0), (1,3,10,8,700), (1,4,11,8,400), (2,5,4,3,450), (3,4,4,4,0),
(3,5,0,0,0), (4,6,15,15,0), (5,6,21,16,600), (5,7,25,22,300),
(5,8,35,30,500), (6,7,0,0,0), (6,8,20,16,500), (7,8,15,12,400)]
n=8; x = cp.Variable(n, pos=True)
y = cp.Variable((n,n), integer=True); fun = 0
for i in range(len(L)):
fun = fun + y[L[i][0]-1,L[i][1]-1] * L[i][4]
obj = cp.Minimize(fun+sum(x)); con =[x[7]-x[0]<=49, y>=0]
for i in range(len(L)):
con.append(x[L[i][1]-1]-x[L[i][0]-1]+y[L[i][0]-1,L[i][1]-1]>=L[i][2])
con.append(y[L[i][0]-1,L[i][1]-1]<=L[i][2]-L[i][3])
prob = cp.Problem(obj, con); prob.solve(solver='GLPK_MI')
print('最优值为', prob.value); print('x的取值为:\n', x.value)
print('y的取值为:\n', y.value); xx=x.value
yy = y.value; z = np.zeros(n); z[-1] = xx[-1]
for k in range(n-1, 0, -1):
z[k-1] = min([z[a[1]-1]-a[2]+yy[a[0]-1,a[1]-1] for a in L if a[0]==k])
es = []; ls = []
for i in range(len(L)):
es.append([L[i][0], L[i][1], xx[L[i][0]-1]])
ls.append([L[i][0], L[i][1], z[L[i][1]-1]-L[i][2]+yy[L[i][0]-1,L[i][1]-1]])
print('es = {}'.format(es))
print('ls = {}'.format(ls))
最优值为 1349.0
x的取值为:
[ 0. 5. 9. 13. 9. 30. 34. 49.]
y的取值为:
[[0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
es = [[1, 2, 0.0], [1, 3, 0.0], [1, 4, 0.0], [2, 5, 5.0], [3, 4, 9.0], [3, 5, 9.0], [4, 6, 13.0], [5, 6, 9.0], [5, 7, 9.0], [5, 8, 9.0], [6, 7, 30.0], [6, 8, 30.0], [7, 8, 34.0]]
ls = [[1, 2, 0.0], [1, 3, 0.0], [1, 4, 4.0], [2, 5, 5.0], [3, 4, 11.0], [3, 5, 9.0], [4, 6, 15.0], [5, 6, 9.0], [5, 7, 9.0], [5, 8, 14.0], [6, 7, 34.0], [6, 8, 30.0], [7, 8, 34.0]]
已知例6.20中各项作业完成的三个估计时间如表6.8所示。如果规定时间为52天,求在规定时间内完成全部作业的概率。进一步,如果完成全部作业的概率大于等于95%,那么工期至少需要多少天?
# Lec0718: CriticalPath0625
import numpy as np
import cvxpy as cp
from scipy.stats import norm
L = np.array([(1,2,3,5,7),(1,3,8,9,16),(1,4,8,11,14),(2,5,3,4,5),
(3,4,2,4,6),(3,5,0,0,0),(4,6,8,16,18),(5,6,18,20,28),(5,7,18,25,32),
(5,8,26,33,52),(6,7,0,0,0),(6,8,11,21,25),(7,8,12,15,18)])
et = (L[:,2]+4*L[:,3]+L[:,4])/6 #计算均值
dt = (L[:,4]-L[:,2])**2/36 #计算方差
n = 8; x = cp.Variable((n,n), integer=True)
m = len(L); fun = 0
for i in range(m):
fun=fun+x[L[i][0]-1,L[i][1]-1]*et[i]
obj = cp.Maximize(fun); con=[x>=0, x<=1]
out = np.where(L[:,0]==1)[0] #起点的相邻顶点
con.append(cp.sum(x[0,L[out,1]-1])==1)
for k in range(2,n):
out = np.where(L[:,0]==k)[0] #顶点k发出弧的顶点
ind = np.where(L[:,1]==k)[0] #顶点k进入弧的顶点
con.append(cp.sum(x[L[ind,0]-1,k-1]) == cp.sum(x[k-1,L[out,1]-1]))
ind = np.where(L[:,1]==n) #终点的相邻顶点
con.append(cp.sum(x[L[ind,0]-1,n-1])==1)
prob = cp.Problem(obj, con); prob.solve(solver='GLPK_MI')
print('最优值为:', prob.value); print('最优解为:\n', x.value)
f = prob.value; xx = x.value; s2=0 #方差的初值
for i in range(m):
s2 = s2 + xx[L[i][0]-1, L[i][1]-1]*dt[i]
s = np.sqrt(s2)
p = norm.cdf(52, f, s) #计算标准差和概率
N = norm.ppf(0.95)*s+f; print('标准差s:', s)
print('概率p:', p); print('需要天数N:', N)
最优值为: 51.0
最优解为:
[[0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
标准差s: 3.1622776601683795
概率p: 0.6240851829770754
需要天数N: 56.201483878755575
本案例的详细教案请访问 Project08钢管订购和运输