(1-5) Dijkstra算法:地铁路径规划系统

1.5  综合实战:地铁路径规划系统

本项目是一个用于帮助乘客快速找到地铁乘车路线的应用程序,该系统基于地铁站点之间的邻接矩阵和线路信息,通过用户输入的起点和终点站,计算出最短的乘车路线,并提供详细的乘车指南,包括乘车线路、换乘站点和总耗时。用户可以方便地在系统中输入起点和终点站,获取到达目的地最快的地铁乘车路线,大大提高了乘客的出行效率和体验。

实例1-5:简易上海市地铁路径规划系统codes/1/SubWay

1.5.1  项目介绍

随着城市的发展和人口的增加,城市交通拥堵问题日益突出,尤其是在大城市中。地铁系统因其高效、快速和环保等特点,成为了城市居民出行的重要方式之一。然而,对于新来的游客或者长时间未使用地铁的居民来说,熟悉地铁线路并找到最优的乘车路径可能会成为一项挑战。

1. 需求分析

  1. 快速规划最优路径:用户需要能够输入起点和终点站,系统能够迅速计算出最优的乘车路线。
  2. 详细的乘车指南:系统应该提供详细的乘车指南,包括乘车线路、换乘站点和总耗时,以方便用户理解和跟随。
  3. 友好的用户界面:系统界面应该简洁、直观,易于操作,让用户能够方便地输入起点和终点,并查看乘车指南。
  4. 实时更新:系统应该能够实时更新地铁线路和站点信息,确保用户获取的信息是最新的。
  5. 可靠性和准确性:系统计算的乘车路线应该准确可靠,用户能够依赖系统提供的指引顺利到达目的地。

2. 功能模块分析

(1)数据加载模块

  1. 功能描述:从地铁线路数据源加载地铁线路、站点和换乘时间等信息。
  2. 实现细节:读取地铁线路数据文件,解析数据并存储为相应的数据结构,如字典、列表等。

(2)路径规划模块

  1. 功能描述:根据用户输入的起点和终点站点,计算出最优的乘车路径。
  2. 实现细节:使用图搜索算法(如Dijkstra算法、A*算法等)在地铁网络图上进行搜索,找到起点到终点的最短路径。

1.5.2  地铁数据文件

文件station_new_data.json提供的是一份地铁站点数据文件,数据内容的格式如下所示。

1号线	富锦路	友谊西路	2
1号线	友谊西路	宝安公路	2
1号线	宝安公路	共富新村	3
1号线	共富新村	呼兰路	3
1号线	呼兰路	通河新村	2
1号线	通河新村	共康路	3
1号线	共康路	彭浦新村	2
1号线	彭浦新村	汶水路	2
1号线	汶水路	上海马戏城	2
1号线	上海马戏城	延长路	2
1号线	延长路	中山北路	3
1号线	中山北路	上海火车站	3
1号线	上海火车站	汉中路	1
1号线	汉中路	新闸路	2
1号线	新闸路	人民广场	3
1号线	人民广场	黄陂南路	2
1号线	黄陂南路	陕西南路	3
1号线	陕西南路	常熟路	2
1号线	常熟路	衡山路	2
1号线	衡山路	徐家汇	3
1号线	徐家汇	上海体育馆	3
1号线	上海体育馆	漕宝路	2
1号线	漕宝路	上海南站	3
1号线	上海南站	锦江乐园	2
1号线	锦江乐园	莲花路	3
1号线	莲花路	外环路	2
1号线	外环路	莘庄	2
2号线	徐泾东	虹桥火车站	2
2号线	虹桥火车站	虹桥2号航站楼	2
2号线	虹桥2号航站楼	淞虹路	7
2号线	淞虹路	北新泾	3
2号线	北新泾	威宁路	2
2号线	威宁路	娄山关路	3
#####省略后面的地铁数据

文件station_new_data.json包含了上海地铁各条线路的站点信息,每条线路的站点信息按照起始站到终点站的顺序排列。具体来说,上面每行包含了以下信息:

  1. 地铁线路名称
  2. 起始站点
  3. 终点站点
  4. 两站之间的距离

可以用这些数据于建立地铁线路的图结构,方便进行路径搜索和规划。需要注意的是,有些站点可能在多条地铁线路上出现,而每个站点的名称应该是唯一的。在实际应用中,这些数据可以被用于地铁线路规划、乘客路径查询等方面。

1.5.3  找出两个站点的最短路径

文件main.py实现了一个地铁路径规划系统,通过读取地铁站点信息和线路信息,构建了地铁站点之间的邻接矩阵。然后,利用 Dijkstra 算法计算从起点到终点的最短路径,并考虑了换乘时间。最终,打印输出了起点到终点的最短路径信息,包括总时间以及经过的线路和站点。

(1)定义了一个名为StageNode的类,表示两个站点之间的线路节点。这个类有四个属性:起点(start)、终点(end)、所用时间(time)和所在线路(line)。这个类的目的是在地铁路线规划等应用中存储和处理站点之间的相关信息。

import json
from collections import defaultdict

class StageNode(object):
    """
    两个站点之间的线路节点类
    """
    # 起点
    start = ''
    # 终点
    end = ''
    # 所用时间,不是同一条线的话,time为999,line为0
    time = 999
    # 所在线路
    line = 0

(2)方法get_matrix用于获取地铁站点之间的邻接矩阵及相关信息。首先从文件中读取地铁站点数据,并根据站点之间的关系构建邻接矩阵。然后,该方法会返回如下4个主要的数据结构:

  1. matrix_li:表示地铁站点之间的邻接矩阵,交点为StageNode类的实例对象,具有四个属性:起点、终点、所用时间和所在线路。
  2. stations_index_li:一个字典,用于通过站名获取其在邻接矩阵中的索引。
  3. line_station_di:一个字典,用于存储每条线路上的所有站点。
  4. line_time_di:一个字典,用于存储每条线路上各站点之间的所需时间。

由此可见,方法get_matrix是地铁路径规划系统中的关键组成部分,用于构建地铁网络图以及存储相关站点和线路信息。

def get_matrix():
    """
    获取邻接矩阵
    :return:
    """
    with open('./data/station_new_data.json', encoding='utf-8') as f:
        lines_di = defaultdict(list)
        # 所有站点
        stations = set()
        # key:线路,value:所有站点
        line_station_di = defaultdict(list)
        # key:线路,value:所有站点的时间
        line_time_di = defaultdict(list)
        for i in f:
            lines = i.strip().split('\t')[0]
            start = i.strip().split('\t')[1]
            end = i.strip().split('\t')[2]
            times = int(i.strip().split('\t')[3])

            stations.add(start)
            stations.add(end)

            lines_di[start].append(lines)
            lines_di[end].append(lines)

            if start not in line_station_di[lines]:
                line_station_di[lines].append(start)

            if end not in line_station_di[lines]:
                line_station_di[lines].append(end)
            line_time_di[lines].append(times)

        stations_li = list(stations)
        stations_li.sort(reverse=True)

        # 站点的index字典,通过站名获取其index
        stations_index_li = {i: ind for ind, i in enumerate(stations_li)}

        # 站点和站点之间的邻接矩阵,交点为StageNode实例对象,具有四个属性
        matrix_li = list()
        for m in stations_li:
            line_li = list()
            for n in stations_li:
                stage_node = StageNode()
                stage_node.start = m
                stage_node.end = n

                # 是否有相同的线路
                same_line_li = list(set([i for i in lines_di.get(m, []) if i in lines_di.get(n, [])]))
                if same_line_li:
                    # 如果有共同的线路,则获取共同线路的所需时间
                    same_time_li = list()
                    for s in same_line_li:
                        index_one = line_station_di[s].index(m)
                        index_two = line_station_di[s].index(n)
                        if index_one > index_two:
                            index_one, index_two = index_two, index_one
                        time_li = line_time_di[s][index_one:index_two]
                        time_num = sum(time_li)
                        same_time_li.append(time_num)

                    li = list(zip(same_time_li, same_line_li))
                    li = sorted(li, key=lambda x: x[0], reverse=False)
                    new_time = li[0][0]
                    new_line = li[0][1]

                    stage_node.time = new_time
                    stage_node.line = new_line

                else:
                    stage_node.time = 999
                    stage_node.line = 0

                line_li.append((stage_node.time, [stage_node]))
            matrix_li.append(line_li)
    return matrix_li, stations_index_li, line_station_di, line_time_di

(3)方法get_line的功能是根据给定的起点和终点获取乘车线路及相关信息。该方法首先确定起点和终点在邻接矩阵中的索引,然后利用 Dijkstra 算法计算从起点到终点的最短路径。在计算过程中考虑了换乘时间,并在路径中添加了换乘节点信息。最后,该方法返回一个字典,其中包含了最短路径的总时间和经过的线路及站点信息。

def get_line(matrix_li, stations_index_li, line_station_di, line_time_di, source, target):
    """获取乘车线路"""
    start_index = stations_index_li[source]
    end_index = stations_index_li[target]

    start_li = matrix_li[start_index]
    path_li = [0 for i in range(len(stations_index_li))]
    path_li[start_index] = 1

    transfer_time = 3
    transfer_node = StageNode()
    transfer_node.start = '换乘'
    transfer_node.end = '换乘'
    transfer_node.time = transfer_time
    transfer_node.line = '换乘'

    for i in range(len(stations_index_li)):
        short_time = 1000
        short_index = 0
        for ind, a in enumerate(start_li):
            if path_li[ind] == 0 and a[0] < short_time:
                short_time = a[0]
                short_index = ind

        path_li[short_index] = 1

        for j in range(len(stations_index_li)):
            if start_li[short_index][0] + transfer_time + matrix_li[short_index][j][0] < start_li[j][0]:
                a = [i for i in start_li[short_index][1]]
                a.append(transfer_node)
                for new_a in matrix_li[short_index][j][1]:
                    a.append(new_a)
                total_time = sum([i.time for i in a])
                start_li[j] = (total_time, a)

    ret_li = []
    ret_di = {'time': start_li[end_index][0], 'line_li': ret_li}
    for route in start_li[end_index][1]:
        start = route.start
        end = route.end
        lines = route.line
        times = route.time
        if start != '换乘':
            line_station_di_reverse = line_station_di[lines][::-1]
            line_time_di_reverse = line_time_di[lines][::-1]

            index_one = line_station_di[lines].index(start)
            index_two = line_station_di[lines].index(end)
            if index_one > index_two:
                index_one = line_station_di_reverse.index(start)
                index_two = line_station_di_reverse.index(end)
                station_li = line_station_di_reverse[index_one:index_two + 1]
                time_li = line_time_di_reverse[index_one:index_two]
            else:
                station_li = line_station_di[lines][index_one:index_two + 1]
                time_li = line_time_di[lines][index_one:index_two]

            one_line_di = {'line': lines, 'time': times, 'station': []}
            for ind, st in enumerate(station_li):
                if ind != station_li.__len__() - 1:
                    start_station = station_li[ind]
                    end_station = station_li[ind + 1]
                    use_time = time_li[ind]
                    one_li = (start_station, end_station, use_time)
                    one_line_di['station'].append(one_li)
            ret_li.append(one_line_di)
    return ret_di

(4)下面这段代码是程序的入口,用于执行地铁路径规划系统。首先调用get_matrix方法获取邻接矩阵和相关的站点信息,然后通过用户输入获取起点和终点站点名称,接着调用get_line方法计算并获取起点到终点的乘车线路及相关信息,最后打印输出计算结果。整个过程实现了用户与系统的交互,用户可以通过输入起点和终点来获取地铁乘车线路及相关信息。

if __name__ == '__main__':
    matrix_li, stations_index_li, line_station_di, line_time_di = get_matrix()
    source = input("请输入起点站:")
    target = input("请输入终点站:")
    transfer_li = get_line(matrix_li, stations_index_li, line_station_di, line_time_di, source, target)
    print(transfer_li)

执行后需要输入起点和终点,例如分别输入“莘庄”和“静安寺”后会输出下面的计算结果:

请输入起点站:莘庄
请输入终点站:静安寺
{'time': 27, 'line_li': [{'line': '1号线', 'time': 22, 'station': [('莘庄', '外环路', 2), ('外环路', '莲花路', 2), ('莲花路', '锦江乐园', 3), ('锦江乐园', '上海南站', 2), ('上海南站', '漕宝路', 3), ('漕宝路', '上海体育馆', 2), ('上海体育馆', '徐家汇', 3), ('徐家汇', '衡山路', 3), ('衡山路', '常熟路', 2)]}, {'line': '7号线', 'time': 2, 'station': [('常熟路', '静安寺', 2)]}]}

上面的输出结果包括了总时间和乘车线路信息,总时间为27分钟。乘车线路分为两段,第一段是乘坐1号线,从莘庄站到常熟路站,共计22分钟,经过了多个站点;第二段是乘坐7号线,从常熟路站到静安寺站,共计2分钟。

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
地铁线路最短路径问题可以使用Dijkstra算法来解决。具体步骤如下: 1. 创建一个包含所有地铁站点的节点集合,并将起点设置为距离起点最近的节点。 2. 对于每个节点,计算从起点到该节点的距离,并将该距离保存在节点中。 3. 对于每个节点,计算从起点到该节点的最短路径,并将该路径保存在节点中。 4. 标记当前节点为已访问。 5. 对于当前节点的每个邻居节点,计算从起点到该邻居节点的距离,并将该距离保存在邻居节点中。 6. 如果从起点到该邻居节点的距离小于该邻居节点中保存的距离,则更新该邻居节点的距离和最短路径。 7. 从未访问的节点中选择距离起点最近的节点作为下一个当前节点,并重复步骤4-6,直到所有节点都被访问过。 下面是一个使用Dijkstra算法求解地铁线路最短路径的Python代码示例: ```python import heapq def dijkstra(graph, start, end): # 初始化距离和路径 distances = {node: float('inf') for node in graph} distances[start] = 0 path = {} # 创建一个优先队列,并将起点加入队列 pq = [(0, start)] while pq: # 取出距离起点最近的节点 (dist, current_node) = heapq.heappop(pq) # 如果当前节点已经访问过,则跳过 if dist > distances[current_node]: continue # 遍历当前节点的邻居节点 for neighbor, weight in graph[current_node].items(): # 计算从起点到邻居节点的距离 distance = dist + weight # 如果从起点到邻居节点的距离小于邻居节点中保存的距离,则更新邻居节点的距离和路径 if distance < distances[neighbor]: distances[neighbor] = distance path[neighbor] = current_node # 将邻居节点加入优先队列 heapq.heappush(pq, (distance, neighbor)) # 如果已经找到终点,则返回最短路径和距离 if current_node == end: path_list = [] while current_node in path: path_list.append(current_node) current_node = path[current_node] path_list.append(start) path_list.reverse() return path_list, distances[end] # 如果无法到达终点,则返回空路径和无穷大的距离 return [], float('inf') ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值