中国省会城市们的最小生成树

MST

(当一个图是连通的时候)最小生成树就是一颗利用已知边把所有点连起来,并且保证边的总长度最短的一颗树。

1
这里我们假设所有的城市之间边就是一条直线。

开始画出来的图是这样的。

然后。。。擦,为啥从拉萨到乌鲁木齐比从兰州还近?然后我意识到这几个西部城市之间距离太远,由于我没有使用劣弧计算产生了较大的偏差。

然后老老实实算劣弧:

1
2
3
4
5
6
设经度是 theta 纬度是 phi
x = cos(phi)*cos(theta)
y = cos(phi)*sin(theta)
z = sin(phi)
劣弧角度 = acos(<v1,v2>)// 求个内积

感觉好多了。连了三条边的城市有:石家庄,太原,郑州,兰州,合肥,贵阳,南昌,澳门(忽略这货)。

其实武汉和郑州都有实力连四条边。话说前一张图跟京广线重合度真高。找不到更多槽点了,也许可以考虑弄一个算法控制枢纽的数量。

画图程序
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
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import mst
cities, edge = mst.get_mst()
v1 = [[cities[x].x, cities[x].y] for x, y in edge]
v1 = np.array(v1)
v2 = [[cities[y].x, cities[y].y] for x, y in edge]
v2 = np.array(v2)
m = Basemap(width=6000000, height=6000000, projection='lcc',
resolution='h', lat_0=30, lon_0=105)
m.readshapefile('bou2_4p', 'conties')
m.drawmapboundary(fill_color='#ffffff')
m.fillcontinents(color='#eeeeee',
lake_color='#689CD2', zorder=0)
x1, y1 = m(v1[:, 0], v1[:, 1])
x2, y2 = m(v2[:, 0], v2[:, 1])
v = [[c.x, c.y] for c in cities]
v = np.array(v)
x, y = m(v[:, 0], v[:, 1])
names = [c.name for c in cities]
m.scatter(x, y, marker='o')
y_offset = 15.0
mpl.rcParams['font.sans-serif'] = ['SimHei']
for i in range(len(cities)):
plt.text(x[i], y[i]+y_offset, names[i].decode('utf8'),
rotation=20,fontsize=6)
for i in range(len(x1)):
plt.plot([x1[i], x2[i]], [y1[i], y2[i]])
plt.show()
求最小生成树的程序
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import math
import heapq
distMat = {}
marked = []
pq = []
class City:
def __init__(self, id, name, longitude, latitude):
self.id = id
self.name = name
self.x = longitude
self.y = latitude
def create_graph(filename):
with open(filename, 'r') as f:
id = 0
cities = []
for line in f.readlines():
arr = line.split(',')
city = City(id, arr[0], float(arr[1]), float(arr[2]))
cities.append(city)
id += 1
return cities
def create_edges(cities):
adj = []
N = len(cities)
for i in range(N):
for j in range(i):
adj.append((j, i, dist(cities[i], cities[j])))
return adj
def visit(adj, v):
global marked, pq
marked[v] = True
for a, b, dist in adj:
if a == v:
if not marked[b]:
heapq.heappush(pq, (dist, (a, b)))
if b == v:
if not marked[a]:
heapq.heappush(pq, (dist, (a, b)))
def dist(city1, city2):
from math import cos
from math import sin
from math import acos
global distMat
if city1.id < city2.id:
pairs = (city1.id, city2.id)
else:
pairs = (city2.id, city1.id)
if not pairs in distMat:
log1 = math.radians(city1.x)
lat1 = math.radians(city1.y)
log2 = math.radians(city2.x)
lat2 = math.radians(city2.y)
x1 = cos(lat1) * cos(log1)
y1 = cos(lat1) * sin(log1)
z1 = sin(lat1)
x2 = cos(lat2) * cos(log2)
y2 = cos(lat2) * sin(log2)
z2 = sin(lat2)
distMat[pairs] = acos(x1 * x2 + y1 * y2 + z1 * z2)
# distMat[pairs] = math.sqrt((city1.x - city2.x) ** 2 +\
# (city1.y - city2.y) ** 2)
return distMat[pairs]
def mst(cities, adj):
global marked, pq, degree
collect = []
N = len(cities)
degree = [0] * N
marked = [False] * N
visit(adj, 0)
while len(pq) > 0:
dist, (a, b) = heapq.heappop(pq)
if marked[a] and marked[b]:
continue
collect.append((a, b))
degree[a] += 1
degree[b] += 1
if not marked[a]: visit(adj, a)
if not marked[b]: visit(adj, b)
return collect
def get_mst():
cities = create_graph('cities')
adj = create_edges(cities)
edge = mst(cities, adj)
return cities, edge
城市列表 cities
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
北京,116.4666667,39.9
上海,121.4833333,31.23333333
天津,117.1833333,39.15
重庆,106.5333333,29.53333333
哈尔滨,126.6833333,45.75
长春,125.3166667,43.86666667
沈阳,123.4,41.83333333
呼和浩特,111.8,40.81666667
石家庄,114.4666667,38.03333333
太原,112.5666667,37.86666667
济南,117,36.63333333
郑州,113.7,34.8
西安,108.9,34.26666667
兰州,103.8166667,36.05
银川,106.2666667,38.33333333
西宁,101.75,36.63333333
乌鲁木齐,87.6,43.8
合肥,117.3,31.85
南京,118.8333333,32.03333333
杭州,120.15,30.23333333
长沙,113,28.18333333
南昌,115.8666667,28.68333333
武汉,114.35,30.61666667
成都,104.0833333,30.65
贵阳,106.7,26.58333333
福州,119.3,26.08333333
台北,121.5166667,25.05
广州,113.25,23.13333333
海口,110.3333333,20.03333333
南宁,108.3333333,22.8
昆明,102.6833333,25
拉萨,91.16666667,29.66666667
香港,114.1666667,22.3
澳门,113.5,22.2

地图文件

评论