洗牌后的结果不同

时间:2015-07-28 11:25:42

标签: python algorithm python-3.x graph shuffle

我想将一个不完整的图分成单独的,未连接的主体。图表的边缘位于列表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'}]

请注意,预期的输出也会不时出现。 (应该总是如此)

我也会欣赏不同的方法,因为这可能不是最好的方法。

2 个答案:

答案 0 :(得分:4)

假设您有三条边[(1, 2), (3, 4), (2, 3)]。 这描述了连接图。

但是,您的代码将首先检查(1, 2),找不到任何内容,因此请将其添加到bodylist

然后,它会查找(3, 4),找不到34,因此请将其添加为新列表。

最后,它会将(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])
相关问题