Python 计算Dijkstra的跳跃次数';s算法?

Python 计算Dijkstra的跳跃次数';s算法?,python,algorithm,numpy,graph-theory,dijkstra,Python,Algorithm,Numpy,Graph Theory,Dijkstra,在numpy中,计算dijkstra算法使用的跳跃次数的最快方法是什么?我有一个10000x1000元素连接性矩阵,并使用scipy.sparse.csgraph.dijkstra计算填充距离矩阵和前置矩阵。我的天真解决方案如下: import numpy as np from numpy.random import rand from scipy.sparse.csgraph import dijkstra def dijkway(dijkpredmat, i, j): """cal

在numpy中,计算dijkstra算法使用的跳跃次数的最快方法是什么?我有一个10000x1000元素连接性矩阵,并使用scipy.sparse.csgraph.dijkstra计算填充距离矩阵和前置矩阵。我的天真解决方案如下:

import numpy as np
from numpy.random import rand
from scipy.sparse.csgraph import dijkstra

def dijkway(dijkpredmat, i, j):
    """calculate the path between two nodes in a dijkstra matrix"""
    wayarr = []
    while (i != j) & (j >= 0):
        wayarr.append(j)
        j = dijkpredmat[i,j]
    return np.array(wayarr)

def jumpvec(pmat,node):
    """calculate number of jumps from one node to all others"""
    jumps = np.zeros(len(pmat))
    jumps[node] = -999
    while 1:
        try:
            rvec = np.nonzero(jumps==0)[0]
            r = rvec.min()
            dway = dijkway(pmat, node, r)
            jumps[dway] = np.arange(len(dway),0,-1)
        except ValueError:
            break
    return jumps

#Create a matrix
mat = (rand(500,500)*20)
mat[(rand(50000)*500).astype(int), (rand(50000)*500).astype(int)] = np.nan
dmat,pmat = dijkstra(mat,return_predecessors=True)

timeit jumpvec(pmat,300)
In [25]: 10 loops, best of 3: 51.5 ms per loop

~50msek/节点可以,但将距离矩阵扩展到10000个节点会将时间增加到~2sek/节点。jumpvec也必须执行10000次

以下代码可以在我的电脑上加速4倍,因为:

  • 使用
    ndarray.item()
    从数组中获取值
  • 使用set对象保存未处理的索引
  • 不要在while循环中创建
    numpy.arange()
Python代码:

def dijkway2(dijkpredmat, i, j):
    wayarr = []
    while (i != j) & (j >= 0):
        wayarr.append(j)
        j = dijkpredmat.item(i,j)
    return wayarr

def jumpvec2(pmat,node):
    jumps = np.zeros(len(pmat))
    jumps[node] = -999
    todo = set()
    for i in range(len(pmat)):
        if i != node:
            todo.add(i)

    indexs = np.arange(len(pmat), 0, -1)
    while todo:
        r = todo.pop()
        dway = dijkway2(pmat, node, r)
        jumps[dway] = indexs[-len(dway):]
        todo -= set(dway)
    return jumps
要进一步加速,您可以使用cython:

import numpy as np
cimport numpy as np
import cython

@cython.wraparound(False)
@cython.boundscheck(False)
cpdef dijkway3(int[:, ::1] m, int i, int j):
    cdef list wayarr = []
    while (i != j) & (j >= 0):
        wayarr.append(j)
        j = m[i,j]
    return wayarr

@cython.wraparound(False)
@cython.boundscheck(False)
def jumpvec3(int[:, ::1] pmat, int node):
    cdef np.ndarray jumps
    cdef int[::1] jumps_buf
    cdef int i, j, r, n
    cdef list dway
    jumps = np.zeros(len(pmat), int)
    jumps_buf = jumps
    jumps[node] = -999

    for i in range(len(jumps)):
        if jumps_buf[i] != 0:
            continue
        r = i
        dway = dijkway3(pmat, node, r)
        n = len(dway)
        for j in range(n):
            jumps_buf[<int>dway[j]] = n - j
    return jumps
输出:

1000 loops, best of 3: 138 µs per loop
100 loops, best of 3: 2.81 ms per loop
100 loops, best of 3: 10.8 ms per loop
这是一个(渐近最优)O(n)算法


创建一组未访问的顶点,最初是源顶点以外的所有顶点。将源的跳转向量项初始化为0,使其自身跳转。当集合不为空时,弹出一个元素v。使用前置矩阵,收集v和列表中的每个后续祖先,直到您找到一个已经访问过的祖先。以相反的顺序遍历列表,将每个节点的跳跃向量条目w设置为其父节点的条目加1,然后从集合中删除w。

对于那些不想看python代码就回答问题的人,这可能有助于解释计算Dijkstra算法使用的“跳跃次数”的含义。当然!Dijkstra算法通过使用通过其他节点的中间步骤来计算两个节点之间的最短距离。例如:A和C之间的距离为5,A和B之间的距离为2,B和C之间的距离为2。然后,旅行A->B->C比直接旅行A->C更短。第一种情况下的跳跃次数是2,第二种情况下的跳跃次数是1。@brorfred:我可能遗漏了一些东西,但跳跃次数似乎可以简单地作为dijkstra的一个特例来解决,带有未加权图,顺便说一下,可以通过广度优先搜索(无需重新访问dijkstra中的旧节点)以更快的速度渐近求解。哇,第一个示例的加速对于更大的矩阵来说更为显著–对于11000个节点,加速一个数量级。非常感谢您的建议!我想我必须花更多的时间在python上。。。
1000 loops, best of 3: 138 µs per loop
100 loops, best of 3: 2.81 ms per loop
100 loops, best of 3: 10.8 ms per loop