我想将一个不完整的图分成单独的,未连接的主体。图表的边缘位于列表edges
中。
代码在改组边的顺序时给出不同的结果。那是为什么?
from random import shuffle
edges = [('7', '9'), ('2', '8'), ('4', '10'), ('5', '9'), ('1', '2'), ('1', '6'), ('6', '10')]
bodylist = []
shuffle(edges)
for edge in edges:
#If at least one node of the edge is anywhere in bodylist, append the new nodes to that list.
try:
index = [i for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body][0]
bodylist[index].append(edge[0])
bodylist[index].append(edge[1])
#If not, make a new list containing the new nodes.
except:
bodylist.append([edge[0], edge[1]])
print([set(x) for x in bodylist])
预期输出:[{'1', '2', '8', '4', '6', '10'}, {'9', '5', '7'}]
部分实际产出:[{'9', '5', '7'}, {'1', '2', '8'}, {'10', '4', '6', '1'}]
[{'9', '7', '5'}, {'6', '2', '1', '8'}, {'6', '10', '4'}]
请注意,预期的输出也会不时出现。 (应该总是如此)
我也会欣赏不同的方法,因为这可能不是最好的方法。
答案 0 :(得分:4)
假设您有三条边[(1, 2), (3, 4), (2, 3)]
。
这描述了连接图。
但是,您的代码将首先检查(1, 2)
,找不到任何内容,因此请将其添加到bodylist
。
然后,它会查找(3, 4)
,找不到3
或4
,因此请将其添加为新列表。
最后,它会将(2, 3)
添加到第一个列表,但它不会回来修复它的错误,它不会意识到(3, 4)
属于同一个主体。
为了解决这个问题,每次向主体添加新边缘时,都可以完全循环通过剩余的边缘,以检查是否存在连接:
while edges:
current_edge = edges.pop(0)
body = {current_edge[0], current_edge[1]}
i = 0
while i < len(edges):
if edges[i][0] in body or edges[i][1] in body:
body.add(edges[i][0])
body.add(edges[i][1])
edges.pop(i) # Edge added, no need to check it again
i = 0 # Restart the loop
else:
i += 1
bodylist.append(body)
您要找的是图表的connected component。
如果您正在寻找有效的算法,则应该查看this answer。
答案 1 :(得分:3)
这是因为你的算法错了。您的算法的问题在于它取决于我们开始创建body列表的边缘。为了解释这一点,我们举一个带有4个边的图的简单示例为 - edges = [('1','2'),('2','3'),('3','4'),('1','4')]
。
第一种情况 -
>>> edges = [('1','2'),('2','3'),('3','4'),('1','4')]
>>> bodylist = []
>>> for edge in edges:
... #If at least one node of the edge is anywhere in bodylist, append the new nodes to that list.
... try:
... index = [i for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body][0]
... bodylist[index].append(edge[0])
... bodylist[index].append(edge[1])
... #If not, make a new list containing the new nodes.
... except:
... bodylist.append([edge[0], edge[1]])
...
>>> print([set(x) for x in bodylist])
[{'4', '1', '3', '2'}]
你得到一个带有顶点的身体 - 1, 2, 3, 4
。为什么?
因为你开始(1,2)将其添加到正文列表中。然后你拿了(2,3),你看到2已经存在于正文列表中添加的项目中,你再次将它添加到同一个项目中,然后继续将它们添加到同一个正文中。
现在,让我们以不同的顺序采用相同的边缘 - edges = [('1','2'),('3','4'),('2','3'),('1','4')]
。结果是 -
>>> edges = [('1','2'),('3','4'),('2','3'),('1','4')]
>>> bodylist = []
>>> .... #same logic
>>> print([set(x) for x in bodylist])
[{'4', '1', '3', '2'}, {'4', '3'}]
正如你可以看到的那样,你有两个不同的身体(显然他们错了)为什么?
你又开始用(1,2),把它作为一个身体添加到bodylist中,然后你拿(3,4),检查一下,你看到没有任何顶点已经存在于任何体内,因此你把它添加到一个单独的身体。取第三个元素(2,3),你会发现它在第一个和第二个体中都存在,但你的逻辑是只取第一个体并将元素添加到那个体中。 (你看到你哪里出错了吗?)
这就是为什么你在洗牌时得到不同的结果,因为顺序对你的逻辑很重要(这是错误的)。
您需要做的是,如果您在多个实体中找到边的顶点,则需要将两个实体合并为一个实体。
另一个建议是,我们不需要在bodylist
中添加列表,而是可以为sets
使用body
。
示例解决方案看起来像 -
from random import shuffle
edges = [('7', '9'), ('2', '8'), ('4', '10'), ('5', '9'), ('1', '2'), ('1', '6'), ('6', '10')]
bodylist = []
shuffle(edges)
for edge in edges:
bodies = [body for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body]
if len(bodies) > 0:
tempset = {edge[0],edge[1]}
for x in bodies:
tempset.update(x)
print('tempset :',tempset)
bodylist.remove(x)
bodylist.append(tempset)
else:
bodylist.append({edge[0],edge[1]})
print([set(x) for x in bodylist])