首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >加速Dijkstra算法

加速Dijkstra算法
EN

Stack Overflow用户
提问于 2020-01-20 23:08:23
回答 2查看 508关注 0票数 0

我有Dijkstra algyrithm:

代码语言:javascript
复制
   # ==========================================================================
# We will create a dictionary to represent the graph
# =============================================================================
graph = {
    'a' : {'b':3,'c':4, 'd':7},
    'b' : {'c':1,'f':5},
    'c' : {'f':6,'d':2},
    'd' : {'e':3, 'g':6},
    'e' : {'g':3, 'h':4},
    'f' : {'e':1, 'h':8},
    'g' : {'h':2},
    'h' : {'g':2}
}

def dijkstra(graph, start, goal):
    shortest_distance = {}     # dictionary to record the cost to reach to node. We will constantly update this dictionary as we move along the graph.
    track_predecessor = {}     # dictionary to keep track of path that led to that node.
    unseenNodes = graph.copy() # to iterate through all nodes
    infinity = 99999999999     # infinity can be considered a very large number
    track_path = []            # dictionary to record as we trace back our journey

    # Initially we want to assign 0 as the cost to reach to source node and infinity as cost to all other nodes
    for node in unseenNodes:
        shortest_distance[node] = infinity
    shortest_distance[start] = 0

    # The loop will keep running until we have entirely exhausted the graph, until we have seen all the nodes
    # To iterate through the graph, we need to determine the min_distance_node every time.
    while unseenNodes:
        min_distance_node = None
        for node in unseenNodes:
            if min_distance_node is None:
                min_distance_node = node
            elif shortest_distance[node] < shortest_distance[min_distance_node]:
                min_distance_node = node

        # From the minimum node, what are our possible paths
        path_options = graph[min_distance_node].items()

        # We have to calculate the cost each time for each path we take and only update it if it is lower than the existing cost
        for child_node, weight in path_options:
            if weight + shortest_distance[min_distance_node] < shortest_distance[child_node]:
                shortest_distance[child_node] = weight + shortest_distance[min_distance_node]
                track_predecessor[child_node] = min_distance_node

        # We want to pop out the nodes that we have just visited so that we dont iterate over them again.
        unseenNodes.pop(min_distance_node)

    # Once we have reached the destination node, we want trace back our path and calculate the total accumulated cost.
    currentNode = goal
    while currentNode != start:
        try:
            track_path.insert(0, currentNode)
            currentNode = track_predecessor[currentNode]
        except KeyError:
            print('Path not reachable')
            break
    track_path.insert(0, start)

    #  If the cost is infinity, the node had not been reached.
    if shortest_distance[goal] != infinity:
        print('Shortest distance is ' + str(shortest_distance[goal]))
        print('And the path is ' + str(track_path))

如果我有少量的节点(就像在代码中),它工作得很好,但我有大约48万个节点的图,根据我的近似计算,它将在7.5小时内在如此大的数组路径中找到,这只是一种方式!我怎么才能让它工作得更快呢?例如,在OSM中,它以秒为单位进行计算!

EN

回答 2

Stack Overflow用户

发布于 2020-01-20 23:32:29

通常,这类事情可以通过使用numba来改进。我做了一个简单的例子来说明如何实现这一点。在pycharm中,它确实输出了很多额外的东西,但这并不是真的那么重要。

它的工作方式是,numba编译您的代码,而不是逐行读取所有内容。对于较短的程序,这会增加几秒钟的运行时间。然而,你说的是几个小时,所以这肯定会让你的代码更快。

代码语言:javascript
复制
# ==========================================================================
# We will create a dictionary to represent the graph
# =============================================================================
from numba import jit

graph = {
    'a' : {'b':3,'c':4, 'd':7},
    'b' : {'c':1,'f':5},
    'c' : {'f':6,'d':2},
    'd' : {'e':3, 'g':6},
    'e' : {'g':3, 'h':4},
    'f' : {'e':1, 'h':8},
    'g' : {'h':2},
    'h' : {'g':2}
}

@jit
def _dijkstra(graph, start, goal):
    shortest_distance = {}     # dictionary to record the cost to reach to node. We will constantly update this dictionary as we move along the graph.
    track_predecessor = {}     # dictionary to keep track of path that led to that node.
    unseenNodes = graph.copy() # to iterate through all nodes
    infinity = 99999999999     # infinity can be considered a very large number
    track_path = []            # dictionary to record as we trace back our journey

    # Initially we want to assign 0 as the cost to reach to source node and infinity as cost to all other nodes
    for node in unseenNodes:
        if node in shortest_distance:
            del shortest_distance[node]
        shortest_distance[node] = infinity
    del shortest_distance[start]
    shortest_distance[start] = 0

    # The loop will keep running until we have entirely exhausted the graph, until we have seen all the nodes
    # To iterate through the graph, we need to determine the min_distance_node every time.
    while unseenNodes:
        min_distance_node = None
        for node in unseenNodes:
            if min_distance_node is None:
                min_distance_node = node
            elif shortest_distance[node] < shortest_distance[min_distance_node]:
                min_distance_node = node

        # From the minimum node, what are our possible paths
        path_options = graph[min_distance_node].items()

        # We have to calculate the cost each time for each path we take and only update it if it is lower than the existing cost
        for child_node, weight in path_options:
            if weight + shortest_distance[min_distance_node] < shortest_distance[child_node]:
                if child_node in shortest_distance:
                    del shortest_distance[child_node]
                if child_node in track_predecessor:
                    del track_predecessor[child_node]
                shortest_distance[child_node] = weight + shortest_distance[min_distance_node]
                track_predecessor[child_node] = min_distance_node

        # We want to pop out the nodes that we have just visited so that we dont iterate over them again.
        unseenNodes.pop(min_distance_node)

    return track_path, track_predecessor, shortest_distance, infinity

def dijkstra(graph, start, goal):
    track_path, track_predecessor, shortest_distance, infinity = _dijkstra(graph, start, goal)

    # Once we have reached the destination node, we want trace back our path and calculate the total accumulated cost.
    currentNode = goal
    while currentNode != start:
        try:
            track_path.insert(0, currentNode)
            currentNode = track_predecessor[currentNode]
        except KeyError:
            print('Path not reachable')
            break
    track_path.insert(0, start)

    #  If the cost is infinity, the node had not been reached.
    if shortest_distance[goal] != infinity:
        print('Shortest distance is ' + str(shortest_distance[goal]))
        print('And the path is ' + str(track_path))

dijkstra(graph, 'a', 'h')

我把它分成dijkstra_dijkstra的原因是我不能让numba编译后半部分。

票数 0
EN

Stack Overflow用户

发布于 2020-01-21 00:06:13

主要的问题似乎是您对unseenNodes的使用。在每次迭代中,通过迭代unseenNodes中的所有节点来搜索node最小化shortest_distance[node],这需要O(V)时间。这是从循环内部完成的,循环迭代V次,因此总体复杂度为O(V²+ E),其中E项表示path_options上的另一个循环,该循环最多将每条边视为恒定次数。

使用诸如heap之类的priority queue数据结构以便在少于O(V)时间内执行“查找和删除最小值”操作是更有效的。请注意,只有在有了指向节点的路径后,才需要将该节点插入到优先级队列中。使用优先级队列实现Dijkstra算法的伪代码可以在Wikipedia上找到。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59826054

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档