简体   繁体   English

在python上的生成树

[英]Spanning tree on python

I have this hw: 我有这个hw:

  1. Read edge list from a file 从文件中读取边缘列表
  2. Turn it into an adjacency list 把它变成一个邻接列表
  3. Output an unweighted, undirected spanning tree of the graph (we can assume starting point is at vertex 0) 输出图的未加权无向生成树(我们可以假设起点位于顶点0)

I have problem getting 3. to output correctly. 我有问题得到3.输出正确。 Namely, file 1 should output [[1],[0,2,3],[1],[1]] and I got [[1,2],[0,3],[0],[1]] which is sort of ok since they are both spanning trees for n=4 from file 1 即,文件1应该输出[[1],[0,2,3],[1],[1]] ,我得到[[1,2],[0,3],[0],[1]] ,因为它们是两个生成树这是有点OK n=4从文件1

but here's the major problem: I don't know what's wrong in my code, for file 2: I get: [[10], [], [10], [10], [], [], [], [], [], [], [0, 3, 2], [], []] 但这是主要的问题:我不知道我的代码有什么问题,对于文件2:我得到: [[10], [], [10], [10], [], [], [], [], [], [], [0, 3, 2], [], []]

Data for the files at end of my code. 我的代码末尾的文件数据。 (edit: starting from tree=[] is where the problems lie, the rest have no issues) (编辑:从tree=[]是问题所在,其余没有问题)

Here's my attempt at the problem: 这是我对这个问题的尝试:

import itertools

edge_i=[]
edge_j=[]
x = []
y = []
edgelist = []
n = int(input("Enter value for n:")) #input n for number of vertices
adjlist = [[] for i in range(n)] #create n sublists inside empty initial adjlist
data = [['0','1'],['2','1'],['0','2'],['1','3']]


for line in data: #for loop for appending into adjacency list the correct indices taken from out of the edgelist
    #(this line won't be needed when hardcoding input) line = line.replace("\n","").split(" ")
    for values in line:
        values_as_int = int(values)
        edgelist.append(values_as_int)



#set of vertices present in this file - pick out only n vertices
verticesset=set(edgelist)
listofusefulvertices=list(verticesset)[0:n]


P = list(itertools.permutations(listofusefulvertices,2))


x.append(edgelist[0::2])
y.append(edgelist[1::2])
x = sum(x,[])
y = sum(y,[])
dataint=zip(x,y)
datatuples=list(dataint)
outdata = set(datatuples)&set(P)
output=list(outdata)


for k in range(len(output)):
    edge_i.append(output[k][0])
    edge_i.append(output[k][1])
    edge_j.append(output[k][1])
    edge_j.append(output[k][0])

for i in range(len(edge_i)):
    u = edge_i[i]
    v = edge_j[i]
    adjlist[u].append(v)
print(adjlist)


tree = []
for vertexNum in range(len(listofusefulvertices)):
    tree.append([])
treeVertices = [0]*n
treeVertices[0]=1
for vertex in range(0,n): #(here the range in skeletal code from school used 1,n but it only worked for me when I used 0,n-1 or 0,n)
    if treeVertices[vertex] == 1:
        for adjVertex in adjlist[vertex]:
            if treeVertices[adjVertex] == 0:
                treeVertices[adjVertex]=1
                tree[adjVertex].append(vertex)
                tree[vertex].append(adjVertex)

print(tree)


#The data from files were: file 1: [['0','1'],['2','1'],['0','2'],['1','3']]
# file 2: [['10','2'],['7','4'],['11','3'],['1','12'],['6','8'],['10','3'],['4','9'],['5','7'],['8','12'],['2','11'],['1','6'],['0','10'],['7','2'],['12','5']]

I didn't wade through all your code, you really should look at the guidance Minimal, complete, verifiable example . 我没有涉及你的所有代码,你真的应该看看指导最小,完整,可验证的例子

However, it is fairly simple to turn an edge-list into a graph, and then use a standard mst algorithm, eg Prim's: 但是,将边缘列表转换为图形,然后使用标准的mst算法(例如Prim's)非常简单:

def create_graph(edgelist):
    graph = {}
    for e1, e2 in edgelist:
        graph.setdefault(e1, []).append(e2)
        graph.setdefault(e2, []).append(e1)
    return graph

# Prim's
def mst(start, graph):
    closed = set()
    edges = []
    q = [(start, start)]
    while q:
        v1, v2 = q.pop()
        if v2 in closed:
            continue
        closed.add(v2)
        edges.append((v1, v2))
        for v in graph[v2]:
            if v in graph:
                q.append((v2, v))
    del edges[0]
    assert len(edges) == len(graph)-1
    return edges

>>> graph = create_graph([[10, 2], [7, 4], [11, 3], [1, 12], [6, 8], [10, 3], [4, 9], [5, 7], [8, 12], [2, 11], [1, 6], [0, 10], [7, 2], [12, 5]])
>>> min_gragh = create_graph(mst(0, graph))
>>> min_graph
{0: [10],
 1: [6],
 2: [11, 7],
 3: [10, 11],
 4: [7, 9],
 5: [7, 12],
 6: [8, 1],
 7: [2, 5, 4],
 8: [12, 6],
 9: [4],
 10: [0, 3],
 11: [3, 2],
 12: [5, 8]}
>>> [sorted(min_graph[k]) for k in sorted(min_graph)]
[[10], [6], [7, 11], [10, 11], [7, 9], [7, 12], [1, 8], [2, 4, 5], [6, 12], [4], [0, 3], [2, 3], [5, 8]]

There are potentially multiple valid MST for a graph, eg your smaller edgelist produces [[2], [2, 3], [0, 1], [1]] , which is also a valid MST but different from your expected output. 对于图形,可能存在多个有效的MST,例如,较小的edgelist产生[[2], [2, 3], [0, 1], [1]] ,这也是有效的MST,但与预期的输出不同。

The problem is in your main processing loop at the end. 问题出在最后的主处理循环中。 You use node 0 as the starting node, but then assume that your connectivity runs in numerical order. 您使用节点0作为起始节点,但随后假设您的连接按数字顺序运行。 You flag all the nodes adjacent to node 0 (only node 10), and then take up node 1 next. 您标记与节点0(仅节点10)相邻的所有节点,然后接下来接收节点1。 That's not yet connected, so you skip it ... but you never come back. 那还没有连接,所以你跳过它......但你永远不会回来。

Here's the code and trace from my low-tech debugging run: 这是我的低技术调试运行的代码和跟踪:

for vertex in range(0,n): #(here the range in skeletal code from school used 1,n but it only worked for me when I used 0,n-1 or 0,n)
    print ("Working on vertex", vertex, treeVertices[vertex] == 1)
    if treeVertices[vertex] == 1:
        for adjVertex in adjlist[vertex]:
            print ("  Adjacent vertex", adjVertex, treeVertices[adjVertex] == 0)
            if treeVertices[adjVertex] == 0:
                treeVertices[adjVertex]=1
                tree[adjVertex].append(vertex)
                tree[vertex].append(adjVertex)

print("Spanning tree", tree)

Output: 输出:

Adjacency list [[10], [12, 6], [11, 7, 10], [11, 10], [9, 7], [7, 12], [8, 1], [5, 4, 2], [6, 12], [4], [0, 3, 2], [2, 3], [1, 8, 5]]

Working on vertex 0 True
  Adjacent vertex 10 True
Working on vertex 1 False
Working on vertex 2 False
Working on vertex 3 False
Working on vertex 4 False
Working on vertex 5 False
Working on vertex 6 False
Working on vertex 7 False
Working on vertex 8 False
Working on vertex 9 False
Working on vertex 10 True
  Adjacent vertex 0 False
  Adjacent vertex 3 True
  Adjacent vertex 2 True
Working on vertex 11 False
Working on vertex 12 False
Spanning tree [[10], [], [10], [10], [], [], [], [], [], [], [0, 3, 2], [], []]

See the problem? 看到问题? The algorithm assumes that the flow of finding the spanning tree will always succeed if you move from available nodes to higher-numbered nodes. 该算法假设,如果从可用节点移动到更高编号的节点,则查找生成树的流将始终成功。 Since this tree requires several "down" moves, you don't get them all. 由于这棵树需要几次“向下”移动,所以你不能全部获得它们。 You start at 0, mark 10, and then skip nodes 1-9. 从0开始,标记10,然后跳过节点1-9。 When you get to 10, you add nodes 2 and 3 ... but you never go back to expand on them, and that's all you get. 当你到达10时,你会添加节点2和3 ......但是你永远不会回过头来展开它们,而这就是你得到的全部。

To get them all, do one of two things: 要完成所有这些,请执行以下两项操作之一:

  1. Put the "unexpanded" nodes on a "to do" list, starting with [0]. 将“未扩展”节点放在“待办事项”列表中,从[0]开始。 Your top for loop changes to a while that continues until that list is empty. 你的顶部循环的变化是持续进行,直到该列表是空的一段时间
  2. Keep your current structure, but add an outer loop that continues until either all nodes are tagged, or no edges get added (no spanning tree found). 保持当前结构,但添加一个持续的外部循环,直到所有节点都被标记,或者没有添加边(未找到生成树)。

Does that get you moving to a solution? 这会让你转向解决方案吗?

I think I may have fixed it, by adding the outer loop while all(treeVertices) == 0: above the for loops for vertex. 我想我可能已经修复了它,通过添加外部循环while all(treeVertices) == 0:在for循环的for循环之上。 Now for n= 13 in the file2 long list, I get this output: [[10], [12, 6], [10, 11, 7], [10], [7, 9], [7, 12], [1], [2, 5, 4], [12], [4], [0, 3, 2], [2], [5, 1, 8]] What I don't understand is, why didn't while any(treeVertices) == 0: work and it had to be all()? 现在,对于file2长列表中的n = 13,我得到了这个输出: [[10], [12, 6], [10, 11, 7], [10], [7, 9], [7, 12], [1], [2, 5, 4], [12], [4], [0, 3, 2], [2], [5, 1, 8]]我不明白的是,为什么没有while any(treeVertices) == 0:工作,它必须是全部()? I thought when the treeVertices list gets partially filled by 1s, it won't contain "all" zeros, but "any" zeroes in it, should send the iterator back into its loop again. 我认为当treeVertices列表被1s部分填充时,它将不包含“all”零,但其中的“any”零,应该再次将迭代器发送回其循环。 I've encountered multiple instances of coding in python now where the useage of any() and all() have had the opposite effect for me where any() acted like all(), and all() acted like any(). 我在python中遇到过多个编码实例,其中any()和all()的使用对我有相反的效果,其中any()的作用类似于all(),而all()的行为与any()相同。 Any idea why? 知道为什么吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM