運籌學練習Python精解——圖與網路

郝hai發表於2024-06-09

練習1

北京(Pe)、東京(T)、紐約(N)、墨西哥(M)、倫敦(L)、巴黎(Pa)各城市之間的航線距離如下表所示。從北京(Pe)乘飛機到東京(T)、紐約(N)、墨西哥城(M)、倫敦(L)、巴黎(Pa)五城市做旅遊,每城市恰去一次再返回北京,應如何安排旅遊線,使旅程最短?

L M N Pa Pe T
L 0 56 35 21 51 60
M 56 0 21 57 78 70
N 35 21 0 36 68 68
Pa 21 57 36 0 51 61
Pe 51 78 68 51 0 13
T 60 70 68 61 13 0

解決這個問題需要使用旅行商問題(TSP)的經典演算法之一。我們可以使用蠻力法來求解,因為這是一個小規模的問題,有6個城市。這種方法將考慮所有可能的路線並找到總距離最短的那個。可以使用Python編寫程式碼來實現這個功能。

from itertools import permutations

# 城市的索引
cities = ["L", "M", "N", "Pa", "T"]

# 城市間的距離矩陣
dist_matrix = [
    [0, 56, 35, 21, 60],  # L
    [56, 0, 21, 57, 70],  # M
    [35, 21, 0, 36, 68],  # N
    [21, 57, 36, 0, 61],  # Pa
    [60, 70, 68, 61, 0],  # T
]

# 北京到其他城市的距離
beijing_distances = [51, 78, 68, 51, 13]

# 將城市名稱對映到索引
city_indices = {city: i for i, city in enumerate(cities)}

# 獲取所有城市的排列
city_permutations = permutations(cities)

# 定義一個函式計算一個路線的總距離
def total_distance(route):
    total_dist = beijing_distances[city_indices[route[0]]]  # 從北京到第一個城市的距離
    n = len(route)
    for i in range(n - 1):
        total_dist += dist_matrix[city_indices[route[i]]][city_indices[route[i + 1]]]
    total_dist += beijing_distances[city_indices[route[-1]]]  # 最後一個城市返回北京的距離
    return total_dist

# 變數初始化
min_distance = float('inf')
best_route = None

# 遍歷所有可能的城市排列
for perm in city_permutations:
    dist = total_distance(perm)
    if dist < min_distance:
        min_distance = dist
        best_route = perm

# 輸出最優路徑和最短距離
print("最優路徑: Pe ->", " -> ".join(best_route), "-> Pe")
print("最短距離:", min_distance)
最優路徑: Pe -> Pa -> L -> N -> M -> T -> Pe
最短距離: 211

練習2

Prim演算法是一種用於在加權連通圖中找到最小生成樹的貪心演算法。演算法從一個初始頂點開始,逐步將未加入最小生成樹的最短邊連線到已加入最小生成樹的頂點集合中,直到所有頂點都加入樹中。每次選擇最短的邊都確保了生成的樹具有最小權重總和。Prim演算法與Dijkstra演算法類似,但Prim演算法著重於生成樹的構建,而Dijkstra演算法則用於尋找單個頂點到其餘頂點的最短路徑。Prim演算法的時間複雜度為O(V^2)或O(ElogV),取決於實現方式。

原圖 網路圖 最小樹
import heapq
import networkx as nx
import matplotlib.pyplot as plt

# 定義圖的鄰接矩陣
graph = {
    'v0': {'v1': 2, 'v2': 1, 'v3': 3, 'v4': 4, 'v5': 4, 'v6': 2, 'v7': 5, 'v8': 4},
    'v1': {'v0': 2, 'v2': 4, 'v8': 1},
    'v2': {'v0': 1, 'v1': 4, 'v3': 1},
    'v3': {'v0': 3, 'v2': 1, 'v4': 1},
    'v4': {'v0': 4, 'v3': 1, 'v5': 5},
    'v5': {'v0': 4, 'v4': 5, 'v6': 2},
    'v6': {'v0': 2, 'v5': 2, 'v7': 3},
    'v7': {'v0': 5, 'v6': 3, 'v8': 5},
    'v8': {'v0': 4, 'v1': 1, 'v7': 5}
}

# 使用Prim演算法計算最小生成樹
def prim(graph, start):
    min_heap = [(0, start, None)]
    mst_edges = []
    total_cost = 0
    visited = set()

    while min_heap:
        cost, node, parent = heapq.heappop(min_heap)

        if node in visited:
            continue

        visited.add(node)
        if parent is not None:
            mst_edges.append((parent, node, cost))
            total_cost += cost

        for neighbor, weight in graph[node].items():
            if neighbor not in visited:
                heapq.heappush(min_heap, (weight, neighbor, node))

    return mst_edges, total_cost

# 計算最小生成樹
mst_edges, total_cost = prim(graph, 'v0')

# 列印最小生成樹的邊和總權重
print("最小生成樹的邊:")
for edge in mst_edges:
    print(edge)
print("最小生成樹的總權重:", total_cost)

# 建立NetworkX圖
G = nx.Graph()

# 新增節點和邊
for node, edges in graph.items():
    for neighbor, weight in edges.items():
        G.add_edge(node, neighbor, weight=weight)

# 使用相同的佈局繪製原圖和最小生成樹圖
pos = nx.spring_layout(G)

plt.figure(figsize=(8, 8))

# 繪製原圖
nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000, font_size=20)  # 放大節點和字型
labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels, font_size=20)  # 放大權重標籤

# 繪製最小生成樹的邊
mst_edges_set = set((u, v) for u, v, _ in mst_edges)
mst_labels = {edge: G.edges[edge]['weight'] for edge in mst_edges_set}
nx.draw_networkx_edges(G, pos, edgelist=mst_edges_set, edge_color='blue', width=4)  # 放大最小生成樹的邊
nx.draw_networkx_edge_labels(G, pos, edge_labels=mst_labels, font_color='blue', font_size=20)  # 放大最小生成樹的權重標籤

plt.title('Graph with Minimum Spanning Tree', fontsize=20)  # 放大標題字型
plt.show()
最小生成樹的邊:
('v0', 'v2', 1)
('v2', 'v3', 1)
('v3', 'v4', 1)
('v0', 'v1', 2)
('v1', 'v8', 1)
('v0', 'v6', 2)
('v6', 'v5', 2)
('v6', 'v7', 3)
最小生成樹的總權重: 13

練習3

原圖
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

# 建立鄰接矩陣
a = np.zeros((11, 11))
a[0, 1] = 2; a[0, 2] = 8; a[0, 3] = 1
a[1, 2] = 6; a[1, 4] = 1
a[2, 3] = 7; a[2, 4] = 5; a[2, 5] = 1; a[2, 6] = 2
a[3, 6] = 9
a[4, 5] = 3; a[4, 7] = 2; a[4, 8] = 9
a[5, 6] = 4; a[5, 8] = 6
a[6, 8] = 3; a[6, 9] = 1
a[7, 8] = 7; a[7, 10] = 9
a[8, 9] = 1; a[8, 10] = 2
a[9, 10] = 4

# 建立圖
s = ['v'+str(i) for i in range(1, 12)]
G = nx.Graph()
G.add_nodes_from(s)
for i in range(11):
    for j in range(11):
        if a[i, j] != 0:
            G.add_edge(s[i], s[j], weight=a[i, j])

# 求解最短路徑
p, d = nx.shortest_path(G, 'v1', 'v11'), nx.shortest_path_length(G, 'v1', 'v11')

# 繪製圖形
pos = nx.spring_layout(G)
plt.figure(figsize=(8, 6))
nx.draw(G, pos, with_labels=True, node_size=1000, node_color='lightblue', font_size=12)
labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
nx.draw_networkx_edges(G, pos, edgelist=[(p[i], p[i+1]) for i in range(len(p)-1)], edge_color='r', width=2)
plt.title('Shortest Path: ' + ' -> '.join(p) + ' (distance = ' + str(d) + ')', fontsize=14)
plt.show()

print("Shortest Path:", p)
print("Distance:", d)
#Floyd演算法
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

# 建立鄰接矩陣
a = np.zeros((11, 11))
a[0, 1] = 2; a[0, 2] = 8; a[0, 3] = 1
a[1, 2] = 6; a[1, 4] = 1
a[2, 3] = 7; a[2, 4] = 5; a[2, 5] = 1; a[2, 6] = 2
a[3, 6] = 9
a[4, 5] = 3; a[4, 7] = 2; a[4, 8] = 9
a[5, 6] = 4; a[5, 8] = 6
a[6, 8] = 3; a[6, 9] = 1
a[7, 8] = 7; a[7, 10] = 9
a[8, 9] = 1; a[8, 10] = 2
a[9, 10] = 4

# Floyd演算法
def floyd_algorithm(a):
    n = len(a)
    inf = float('inf')

    # 初始化距離矩陣
    d = np.copy(a)
    for i in range(n):
        for j in range(n):
            if i != j and d[i, j] == 0:
                d[i, j] = inf

    # 計算最短路徑
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if d[i, k] + d[k, j] < d[i, j]:
                    d[i, j] = d[i, k] + d[k, j]

    return d

# 求解最短路徑
d = floyd_algorithm(a)
shortest_path = [1, 2, 5, 6, 3, 7, 10, 9, 11]
distance = d[0, 10]

# 建立圖
G = nx.Graph()
G.add_nodes_from(range(1, 12))
for i in range(11):
    for j in range(11):
        if a[i, j] != 0:
            G.add_edge(i+1, j+1, weight=a[i, j])

# 繪製圖形
pos = nx.spring_layout(G)
plt.figure(figsize=(8, 6))
nx.draw(G, pos, with_labels=True, node_size=1000, node_color='lightblue', font_size=12)
labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
plt.title('Shortest Path: ' + ' -> '.join(map(str, shortest_path)) + ' (distance = ' + str(distance) + ')', fontsize=14)
plt.show()

print("Shortest Path:", shortest_path)
print("Distance:", distance)

練習4

相關文章