Djikstra的算法不适用于python中的堆

时间:2019-07-07 21:21:48

标签: python python-3.x algorithm heap greedy

我想使用堆来实现Djikstra算法,以解决挑战问题in this file at this page's module->用于编程项目的测试用例和数据集->编程问题9.8和10.8:实现Dijkstra的算法

我已经使用堆和朴素的方法实现了Djikstra算法。天真的方法适用于挑战问题,但使用堆的方法却有不同且错误的结果。

我创建了一个函数testcase(),它返回具有挑战问题的节点,顶点和权重的networkx图。 我将此testcase()返回的图传递给下面的Djikstra算法实现。

使用天真的实现的Djikstra算法:

%matplotlib inline
import networkx as nx
import heapq
import numpy as np

def undirected_djikstra_naive(DG, source, destination):
    X = [source]
    A = {}
    A[source] = 0
    all_nodes = list(DG.nodes)
    minimum_2nd_overall = float('inf')
    w_overall = float('inf')
    print("Naive:")
    while len(X) != len(all_nodes):
        for edge in list(DG.edges):
            if (edge[0] in X) and (edge[1] not in X):
                edge_tail = edge[0]
                edge_head = edge[1]
            elif (edge[1] in X) and (edge[0] not in X):
                edge_tail = edge[1]
                edge_head = edge[0]
            if ((edge_tail in X) and (edge_head not in X)) :
                dji_greedy = A[edge_tail] + DG.edges[edge_tail, edge_head]['weight'] #djikstra's greedy criterion
                if edge_head not in A:
                    A[edge_head] = dji_greedy
                elif dji_greedy < A[edge_head]:
                    A[edge_head] = dji_greedy
                if dji_greedy < minimum_2nd_overall:
                    minimum_2nd_overall = dji_greedy
                    w_overall = edge_head  
        minimum_2nd_overall = float('inf')
        X.append(w_overall)
    #print("Printing minimum distances from starting vertex {}".format(source))
    B = []
    for node in A:
        if node in destination:
            #print("Vertex {} is at distance {}".format(node, A[node]))
            B.append([node, A[node]])
    B = sorted(B)
    return B

使用堆实现的Djikstra算法:

%matplotlib inline
import networkx as nx
import heapq
import numpy as np

def undirected_djikstra_heap(DG, source, destination):
    #Heap implementation which does the following
    #
    # 1. For vertices in X, find all edges originating from them to all vertices not in X
    # 2. Keep track of minimum value of len(w) + lwv
    # 3. Return w, v and lwv
    X = [source]
    minHeap = []
    heapq.heappush(minHeap, [0, source])
    all_nodes = list(DG.nodes)
    for node in all_nodes:
        DG.nodes[node]['shortest_dist'] = float('inf') 
    print("Heap:")
    while len(minHeap) != 0:
        w = heapq.heappop(minHeap)
        #print(minHeap)
        X.append(w[1])
        DG.nodes[w[1]]['shortest_dist'] = w[0]
        for edge in list(DG.edges):
            if (edge[0] == w[1]) and (edge[1] not in X):
                edge_tail = edge[0]
                edge_head = edge[1]
            elif (edge[1] == w[1]) and (edge[0] not in X):
                edge_tail = edge[1]
                edge_head = edge[0]
            else:
                continue
            if ((edge_tail == w[1]) and (edge_head not in X)) : # node that has just been popped should be the tail
                dji_greedy = w[0] + DG.edges[edge_tail, edge_head]['weight'] #djikstra's greedy criterion
                if len(minHeap) == 0:
                    heapq.heappush(minHeap, [dji_greedy, edge_head])
                    continue
                singlenp = [i[1] for i in minHeap]
                if edge_head not in singlenp:
                    heapq.heappush(minHeap, [dji_greedy, edge_head])
                else:
                    dest_idx = singlenp.index(edge_head)
                    if dji_greedy < minHeap[dest_idx][0]:
                        minHeap[dest_idx] = minHeap[0]
                        heapq.heappop(minHeap)
                        heapq.heappush(minHeap, [dji_greedy, edge_head])
    #print("Printing minimum distances from starting vertex \'{}\'".format(source))
    B = []
    for node in all_nodes:
        if node in destination:
            #print("Vertex {} is at distance {}".format(node, DG.nodes[node]['shortest_dist']))
            B.append([node, DG.nodes[node]['shortest_dist']])
    B = sorted(B)
    return B


调用和声明算法:

challenge_graph_g = testcase()
start_vert = 1
dest_verts = challenge_graph_g.nodes
heap_dji = undirected_djikstra_heap(challenge_graph_g, start_vert, dest_verts)
naive_dji = undirected_djikstra_naive(challenge_graph_g, start_vert , dest_verts)

for a in range(len(dest_verts)):
    assert heap_dji[a][1] == naive_dji[a][1], "Dist Mismatch at Vertex {} dist heap {}, Vertex {} dist naive {}".format(heap_dji[a][0], heap_dji[a][1], naive_dji[a][0], naive_dji[a][1])

我得到顶点2输出的断言错误,如下所示:

AssertionError:“顶点2”分区3428,“顶点2”原始2971处的Dist不匹配

我不了解我的堆实现失败的原因和地点。请帮忙。


解决方案:

在heapq上进行手动插入/删除操作后,堆化可迭代对象。在堆上进行minHeap [dest_idx] = minHeap [0]手动更新后执行了heapq.heapify(minHeap)。

修改后的代码:

使用堆实现的Djikstra算法:

%matplotlib inline
import networkx as nx
import heapq
import numpy as np

def undirected_djikstra_heap(DG, source, destination):
    #Heap implementation which does the following
    #
    # 1. For vertices in X, find all edges originating from them to all vertices not in X
    # 2. Keep track of minimum value of len(w) + lwv
    # 3. Return w, v and lwv
    X = [source]
    minHeap = []
    heapq.heappush(minHeap, [0, source])
    all_nodes = list(DG.nodes)
    for node in all_nodes:
        DG.nodes[node]['shortest_dist'] = float('inf') 
    print("Heap:")
    while len(minHeap) != 0:
        w = heapq.heappop(minHeap)
        #print(minHeap)
        X.append(w[1])
        DG.nodes[w[1]]['shortest_dist'] = w[0]
        for edge in list(DG.edges):
            if (edge[0] == w[1]) and (edge[1] not in X):
                edge_tail = edge[0]
                edge_head = edge[1]
            elif (edge[1] == w[1]) and (edge[0] not in X):
                edge_tail = edge[1]
                edge_head = edge[0]
            else:
                continue
            if ((edge_tail == w[1]) and (edge_head not in X)) : # node that has just been popped should be the tail
                dji_greedy = w[0] + DG.edges[edge_tail, edge_head]['weight'] #djikstra's greedy criterion
                if len(minHeap) == 0:
                    heapq.heappush(minHeap, [dji_greedy, edge_head])
                    continue
                singlenp = [i[1] for i in minHeap]
                if edge_head not in singlenp:
                    heapq.heappush(minHeap, [dji_greedy, edge_head])
                else:
                    dest_idx = singlenp.index(edge_head)
                    if dji_greedy < minHeap[dest_idx][0]:
                        minHeap[dest_idx] = minHeap[0]
                        heapq.heapify(minHeap)    # Added this single line
                        heapq.heappop(minHeap)
                        heapq.heappush(minHeap, [dji_greedy, edge_head])
    #print("Printing minimum distances from starting vertex \'{}\'".format(source))
    B = []
    for node in all_nodes:
        if node in destination:
            #print("Vertex {} is at distance {}".format(node, DG.nodes[node]['shortest_dist']))
            B.append([node, DG.nodes[node]['shortest_dist']])
    B = sorted(B)
    return B


使用此修改后的代码,Djikstra算法的天真的实现和堆实现在挑战数据集上给出了相同的结果。

0 个答案:

没有答案