问题描述:
用搜索算法模拟解决地铁换乘问题!
例如:
输入:search('徐家汇', '人民广场')
输出: 徐家汇->A->B-C->人民广场
import random
import networkx as nx
from collections import defaultdict
import matplotlib.pyplot as plt
为了简化,这里不使用真实的地铁数据,而采用简单的随机生成的数据来进行模拟
station = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
station = [i for i in station]
#station
def generate_lines(station, num=4):
lines = []
step = len(station)//num
start,end = 0,step
cross_staions = station[-7:]
station = station[:-7]
step = len(station)//num
for i in range(num):
lines.append(station[start:end])
new_step = step + random.randint(-1,1)
start = end
end += new_step
if i==num-1:
end = len(station)
for line in lines:
insert_point = random.sample(cross_staions, random.randint(2,4))
for p in insert_point:
line.insert(random.randint(1,len(line)-2), p)
return lines
lines = generate_lines(station)
绘制一下生成随机地铁路线
G = nx.Graph()
colors = ['r','b','g','k','y']
for index,line in enumerate(lines):
for i in range(len(line)-1):
G.add_edge(line[i],line[i+1],color=colors[index])
# pos = nx.circular_layout(G)
pos = nx.fruchterman_reingold_layout(G)
edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
plt.figure(figsize=(10,5))
nx.draw(G,pos=pos,edges=edges, edge_color=colors, width=2,node_size=30,with_labels=True)
![bcf335e4a86a691afb96aad56841c3dd.png](https://i-blog.csdnimg.cn/blog_migrate/c52c5c89c92f68464cee3f8ab23fdb65.png)
生成图信息,即无向图的连接信息(每个节点连接的节点是什么)
graph_info = defaultdict(list)
for line in lines:
for i,value in enumerate(line):
if i<len(line)-1:
graph_info[value].append(line[i+1])
if i>0:
graph_info[value].append(line[i-1])
标准的广度优先和深度优先遍历算法
def bdfs(graph, start):
visited = [start]
seen = set()
path = []
while visited:
froninter = visited.pop(0) # 模拟队列
if froninter in seen: continue #如果元素已经被访问则跳过本次
for successor in graph[froninter]: #遍历所有连接点
if successor in seen: continue #如果当前元素连接点被访问则跳过本次
# visited = visited + [successor] # 每次访问旧点(和之前pop(0)对应) -> breath first
visited = [successor] + visited # 每次访问新点 -> depth first
# 所以说,这个扩展顺序其实是决定了我们的深度优先还是广度优先
seen.add(froninter)
path.append((froninter))
return path
a = bdfs(graph_info,"F")
以上可以验证一下深度优先和广度优先算法的正确性!
算法改进
从标准的广度优先算法改进成可以搜索A->B路径的算法,我们对每次访问的节点进行判断是否到达了目标点,如果到达则搜索完毕,返回路径
def search(start, destination, connection_grpah, sort_candidate, by_the_way =[]):
#默认是深度优先的话容易一条路走到黑,
pathes = [[start]] #每走一步都添加一个新的路径
visitied = set()
while pathes:
path = pathes.pop(0) #获取当前要访问的路径, 控制深度 广度path = pathes.pop()则变成深度优先
froninter = path[-1] #获取当前达到的节点
if froninter in visitied: continue
for city in connection_grpah[froninter]:
if city in path: continue # eliminate loop 防止环形,也防止重复访问,和city in visitied有同样效果
new_path = path + [city]
pathes.append(new_path)
if city == destination: return new_path
visitied.add(froninter)
##对每次新的路径进行排序
pathes = sort_candidate(pathes, by_the_way)
def transfer_stations_first(pathes,*arg):
##总站数最少策略
return sorted(pathes, key=len)
def transfer_as_much_possible(pathes,*arg):
##总站数最多策略
return sorted(pathes, key=len, reverse=True)
def pretty_print(station):
print(len(station))
print(' ->'.join(station))
pretty_print(search('F', 'P', graph_info, sort_candidate=transfer_stations_first))
5
F ->E ->V ->X ->P
完成,可以达到预期效果
总结
该算法还有许多可以改进的地方
- 如果要添加中途点,那么搜索算法如何改进?
- 如果假如站与站之间的位置信息(两站时间消耗),搜索算法如何修改?
- 如果想最少换乘,搜索算法如何改进?
进一步改进会越来越接近地图里面的效果