python_迪杰斯特拉算法(Dijkstra算法)计算深圳地铁历年最短路径

import sys
import io

import numpy as np
import pandas as pd
'''
(1)迪杰斯特拉算法(Dijkstra算法)

'''
from utils.read_write import readJson

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8')
sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding='utf-8')


# 判断是否为环线
def iscircle(mlist):
    if mlist[0] == mlist[-1]:
        return True
    return False


# 判断是否为换乘点
def istransport(station):
    if len(datanum[station]) > 1:
        return True
    return False


# 得到换线路
def destransport(station):
    return datanum[station]


def changeline(p1, p2):
    line1 = datanum[p1]
    line2 = datanum[p2]
    a = []
    for i1 in data[line1[0]]:
        if istransport(i1):
            ways = destransport(i1)
            for i2 in line2:
                if i2 in ways:
                    a.append(i1)
                    return i1
    return None


class GraphError(ValueError):
    pass


class Graph:
    def __init__(self, mat, unconn=0):
        vnum = len(mat)
        for x in mat:
            if len(x) != vnum:  # 检查是否是方阵
                raise ValueError("Argument for 'Graph'.")
        self._mat = [mat[i][:] for i in range(vnum)]  # 赋值mat到self._mat
        self._unconn = unconn
        self._vnum = vnum

    def vertex_num(self):
        return self._vnum

    def _invalid(self, v):
        return 0 > v or v >= self._vnum

    def add_vertex(self):  # 并未计划支持增加顶点,所以直接定义为错误,要增加顶点需要增加一行矩阵一列
        raise GraphError("Adj-Matrix does not support 'add_vertex'.")

    def add_edge(self, vi, vj, val=1):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        self._mat[vi][vj] = val

    def get_edge(self, vi, vj):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        return self._mat[vi][vj]

    # 记录已经构造的表
    # 用静态方法构造结点表
    def out_edges(self, vi):
        if self._invalid(vi):
            raise GraphError(str(vi) + " is not a valid vertex.")
        return self._out_edges(self._mat[vi], self._unconn)

    @staticmethod
    def _out_edges(row, unconn):
        edges = []
        for i in range(len(row)):
            if row[i] != unconn:
                edges.append((i, row[i]))
            return edges


class GraphAL(Graph):
    def __init__(self, mat=None, unconn=0):
        if mat is None:
            mat = []
        vnum = len(mat)
        for x in mat:
            if len(x) != vnum:
                raise ValueError("Argument for 'GraphAL'.")
        self._mat = [Graph._out_edges(mat[i], unconn) for i in range(vnum)]
        self._vnum = vnum
        self._unconn = unconn

    def add_vertex(self):  # 增加新节点时安排一个新编号
        self._mat.append([])
        self._vnum += 1
        return self._vnum - 1

    def add_edge(self, vi, vj, val=1):
        if self._vnum == 0:
            raise GraphError("cannot add edge to empty graph")
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        row = self._mat[vi]
        i = 0
        while i < len(row):
            if row[i][0] == vj:  # 更新mat[vi][vj]的值
                self._mat[vi][i] = (vj, val)
                return
            if row[i][0] > vj:  # 原来如果没有到vj的边,退出循环,加入边
                break
            i += 1
        self._mat[vi].insert(i, (vj, val))

    def get_edge(self, vi, vj):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        for i, val in self._mat[vi]:
            if i == vj:
                return val
        return self._unconn

    def out_edges(self, vi):
        if self._invalid(vi):
            raise GraphError(str(vi) + " is not a valid vertex.")
        return self._mat[vi]


def all_shortest_path(graph):
    import numpy as np
    vnum = graph.vertex_num()
    a = [[graph.get_edge(i, j) for j in range(vnum)] for i in range(vnum)]
    nvertex = [[-1 if a[i][j] == np.inf else j for j in range(vnum)] for i in range(vnum)]
    for k in range(vnum):
        for i in range(vnum):
            for j in range(vnum):
                if a[i][j] > a[i][k] + a[k][j]:
                    a[i][j] = a[i][k] + a[k][j]
                    nvertex[i][j] = nvertex[i][k]
    return (a, nvertex)


def find_shortest_path(graph, start, end, path=[]):
    '查找最短路径'
    path = path + [start]
    if start == end:
        return path
    if not start in graph.keys():
        return None
    shortest = None
    for node in graph[start]:
        if node not in path:
            newpath = find_shortest_path(graph, node, end, path)
            if newpath:
                if not shortest or len(newpath) < len(shortest):
                    shortest = newpath
    return shortest


def find_all_paths(graph, start, end, path):
    '查找所有的路径'
    path = path + [start]
    if start == end:
        return [path]
    if not start in graph.keys():
        return []
    paths = []
    for node in graph[start]:
        if node not in path:
            newpaths = find_all_paths(graph, node, end, path)
            for newpath in newpaths:
                paths.append(newpath)
    return paths


class PrioQueueError(ValueError):
    pass


# 基于堆的优先队列类,在尾端加入元素,首端作为堆顶,见peek等
class PrioQueue:
    def __init__(self, elist=None):
        if elist is None:
            elist = []
        self._elems = list(elist)
        if elist:
            self.buildheap()

    def buildheap(self):
        end = len(self._elems)
        for i in range(end // 2, -1, -1):
            self.siftdown(self._elems[i], i, end)

    def is_empty(self):
        return not self._elems

    def peek(self):
        if self.is_empty():
            raise PrioQueueError("in peek")
        return self._elems[0]

    def enqueue(self, e):
        self._elems.append(None)
        self.siftup(e, len(self._elems) - 1)

    def siftup(self, e, last):
        elems, i, j = self._elems, last, (last - 1) // 2
        while i > 0 and e < elems[j]:
            elems[i] = elems[j]
            i, j = j, (j - 1) // 2
        elems[i] = e

    def dequeue(self):
        if self.is_empty():
            raise PrioQueueError("in dequeue")
        elems = self._elems
        e0 = elems[0]
        e = elems.pop()
        if len(elems) > 0:
            self.siftdown(e, 0, len(elems))
        return e0

    def siftdown(self, e, begin, end):
        elems, i, j = self._elems, begin, begin * 2 + 1
        while j < end:
            if j + 1 < end and elems[j + 1] < elems[j]:
                j += 1
            if e < elems[j]:
                break
            elems[i] = elems[j]
            i, j = j, 2 * j + 1
        elems[i] = e


# Dijkstra算法实现最短路径查找
def dijkstra_shortest_pathS(graph, v0, endpos):
    vnum = 0
    # 统计路径的个数
    for i in adjoinPaths.keys():
        vnum += 1
    # print(vnum)
    # vnum = graph.vertex_num()
    assert 0 <= v0 < vnum
    paths = [None] * vnum  # 长为vnum的表记录路径
    count = 0
    # 求解最短路径的候选边集记录在优先队列cands中(p,v,v')v0经过v到v'的最短路径长度为p,
    # 根据p的大小排序,保证选到最近的未知距离顶点
    cands = PrioQueue([(0, v0, endpos)])
    while count < vnum and not cands.is_empty():
        plen, u, vmin = cands.dequeue()  # 取路径最短顶点
        # print(u, vmin)
        if paths[vmin]:  # 如果这个点的最短路径已知,则跳过
            continue
        paths[vmin] = (u, plen)  # 新确定最短路径并记录
        for v in graph[vmin]:  # 遍历经过新顶点组的路径
            if not paths[v]:  # 如果还不知道最短路径的顶点的路径,则记录
                cands.enqueue((plen + 1, vmin, v))
        count += 1
        # print(paths)
    return paths


def getRoute(year):
    #  站点编号	站点名称		线路  	是否换乘站
    # station = pd.read_csv('D:\data\地铁数据\SZ_Metro\metro_station_2020.txt', sep='\t',
    #                         usecols=[1, 2, 4, 5],
    #                         encoding='gbk')
    src = 'D:\学习文件\项目文件\data\SZ_Metro_LineSUM_Stoppoint\\'
    yearStop = readJson(src + 'SZ_Metro_LineSUM_Stoppoint_{}_GCJ02.json'.format(year))
    features = yearStop['features']
    for feature in features:
        properties = feature['properties']
        CName = properties['CName']
        NAME = properties['NAME']
        shun = int(NAME.split('-')[1])
        line = properties['line']
        if CName not in datanum.keys():
            datanum[CName] = []
        datanum[CName].append(line)
        data[line].insert(shun,CName)


'''
data:{1: ['赤湾站', '后海站', '世界之窗站', '福田站', '市民中心站', '大剧院站', '华...
datanum:{'蛇口港站': [0], '赤湾站': [1], '东角头站': [0], '海上世
STATION-NUM:{'蛇口港站': 0, '赤湾站': 1, '东角头站': 2, '海上世界站': 3, '水湾站': 
'''
STATION_NUM = {}  # 字典,站名到数字编号的对应
data = {'01号线': [], '02号线': [], '03号线': [], '04号线': [], '05号线': [], '07号线': [],
        '09号线': [], '11号线': []}
datanum = {}  # 邻接表,啊哈就先这么叫他吧
for line in data.keys():
    for i in range(33):
        data[line].append('0')
getRoute('2018')
for line in data.keys():
    for i in range(33):
        data[line].remove('0')

i = 0
STATIO = {}  # 字典,对应数字到站名的对应
for datai in datanum.keys():
    STATION_NUM[datai] = i
    STATIO[i] = datai
    i += 1
I = i

# mat=np.zeros([i,i])
mat = np.full([i, i], np.inf)
RouteGraph = Graph(mat)
print(STATION_NUM)
print(datanum)
routee = {}
# 生成所有邻接矩阵
for key in data.keys():
    datai = data[key]
    for i in range(1, len(datai) - 1):
        # RouteGraph.add_vertex()
        v1 = STATION_NUM[datai[i]]
        v2 = STATION_NUM[datai[i + 1]]
        v3 = STATION_NUM[datai[i - 1]]
        RouteGraph.add_edge(v1, v2, 1)
        RouteGraph.add_edge(v2, v1, 1)
        RouteGraph.add_edge(v3, v1, 1)
        RouteGraph.add_edge(v1, v3, 1)
    if iscircle(datai):
        # RouteGraph.add_vertex()
        v1 = STATION_NUM[datai[0]]
        v2 = STATION_NUM[datai[-2]]
        RouteGraph.add_edge(v1, v2, 1)
        RouteGraph.add_edge(v2, v1, 1)

# 注意的是这里输入的graph参数如下定义:
adjoinPaths = {}
for i in range(I):
    for j in range(I):
        if RouteGraph.get_edge(i, j) == 1:
            start = STATIO[i]
            end = STATIO[j]
            if i not in adjoinPaths.keys():
                adjoinPaths[i] = [j]
            else:
                adjoinPaths[i].append(j)
print(adjoinPaths)

startpos = '横岗站'
endpos = '罗湖站'
s1 = STATION_NUM[startpos]
e1 = STATION_NUM[endpos]
print(startpos, endpos)
paths = dijkstra_shortest_pathS(adjoinPaths, s1, e1)
# 找到最短路径所经过的站点名
b = []
p = paths[e1][0]
b.append(STATIO[p])
while True:
    p1 = paths[p][0]
    p = p1
    if p == s1:
        break
    b.append(STATIO[p])
print(b)

如需数据示例和帮助,请私聊我!

猜你喜欢

转载自blog.csdn.net/qq_30803353/article/details/106130390
今日推荐