【问题标题】:Why does my code take different values when i switch the order in a set (knowing that order doesn't matter with sets)当我在一组中切换顺序时,为什么我的代码会采用不同的值(知道顺序与组无关)
【发布时间】:2019-06-07 17:05:14
【问题描述】:

我正在尝试实现一种返回图形边缘的方法,由邻接列表/字典表示。

所以要遍历字典,首先我遍历键,然后遍历存储在相应键中的每个值。在嵌套的 for 循环中,我有一个条件,如果特定的边,比如 (a,b) 不在边的集合中,则将其添加到集合中——否则通过。在我的第一次运行中,该方法采用了相同的边——也就是说,在一组边中,有 (a,b) 和 (b,a)。

class Graph():
    def __init__(self, grph={}):
        self.graph = grph

    def get_vertices(self):
        for keys in self.graph:
            yield keys

    def get_edges(self):
        edges = set()
        for key in self.graph:
            for adj_node in self.graph[key]:
                if (key, adj_node) not in edges:
                    edge = (key, adj_node)
                    edges.add(edge)
                else:
                    pass
        return edges


def main():
    graph1 = {
        'A': ['B','C','D'],
        'B': ['A','E'],
        'C': ['A', 'D'],
        'D': ['A', 'C'],
        'E': ['B'],
    }
    graph_one = Graph(graph1)
    print(list(graph_one.get_vertices()))
    print(graph_one.get_edges())

if __name__ =='__main__':
    main()

输出是:

{('A','B'),('D','A'),('B','A'),('B','E'),('A',' D'),('D','C'),('E','B'),('C','D'),('A','C'),('C',' A')}

所以我所做的是,我只是更改了 if 语句:

"if (adj_node, key) not in edges:"

def get_edges(self):
        edges = set()
        for key in self.graph:
            for adj_node in self.graph[key]:
                if (adj_node, key) not in edges:
                    edge = (key, adj_node)
                    edges.add(edge)
                else:
                    pass
        return edges

现在的输出是:

{('C','D'),('A','B'),('E','B'),('A','C'),('A',' D')}

我很好奇为什么会这样,如果你们能向我解释一下,我将不胜感激。提前致谢!

【问题讨论】:

  • 您的意思是sort 数据或使用带有排序数据的辅助集来避免插入。 key 和 adj_node 的类型是什么?
  • 无论如何检查都无关紧要,因为该集合只能包含每个值之一,因此您可以多次添加相同的值,并且您的集合的末尾仍然只有一个实例。
  • 对不起,我忘了说(或强调,我想)我不是在问如何删除重复项!我只是想知道为什么当我更改 if 语句的条件时它忽略了重复项
  • 没关系,我的评论与您的​​要求有些相干,但我仍然认为它是有效的。很可能我没有看到全貌,但我根本看不到检查的目的。
  • 这么简单的解释是set 将只保存每个值的一个实例,而您使用if 来检查相反方向的元组,因此实际上避免了两个方向的重复。

标签: python python-3.x data-structures


【解决方案1】:

我认为它只需要一点改变,试试这个:

def get_edges(self):
    edges = set()
    for key in self.graph:
        for adj_node in self.graph[key]:
            if ((key, adj_node) not in edges) and ((adj_node, key) not in edges):
                edge = (key, adj_node)
                edges.add(edge)
            else:
                pass
    return edges

更新:
所以它是一个 Undigraph。
是我把事情复杂化了。
而且你的方式实际上比我检查两种方式更好。

您的代码成功的原因是set 将只包含一个任何值的实例。
所以每次执行add,如果已经存在相同的元组,它根本不会改变集合。
而且您已经使用if 检查了相反方向的元组,因此它不会创建重复的边缘。

例如(a, b)碰到if检查时,会检查(b,a)是否存在于集合中,如果存在则通过。如果不存在,则在集合中添加 (a, b),如果 (a, b) 存在,则集合不会改变,因为集合中只有一个实例。
稍后当循环到 (b, a) 时,由于 (a, b) 已经在集合中,if 将为 false 并通过。
所以通过这种方式,集合是安全的,没有重复边。

【讨论】:

  • 我仍然不明白检查的重点,因为该集合只包含一个任何值的实例
  • 所以只需将它们添加到集合中。 if 检查防止了什么? “如果它不在系列中,添加它”与我所看到的“将它添加到系列并让系列机制意味着我只有一个实例”没有什么不同
  • 或者,两者都加?这个问题有 3 个赞成票,所以我猜是我在这里遗漏了一些东西,但我真的没有看到 :)
  • @roganjosh 这正是 OP 想要避免的,我想这个问题是关于什么的。
  • @FrancisMagnusson 这没什么好遗憾的。 SO 的一个重要而有趣的部分是帮助人们调整他们的思维:)
【解决方案2】:

当我们说集合没有顺序或顺序无关紧要时,这意味着{x, y} == {y, x}。但是(a, b)(b, a) 是元组,顺序对它们很重要,所以(a, b) != (b, a){(a, b), (b, a)} 是一个包含两个不同元素的集合,尽管它等于{(b, a), (a, b)}

当您的代码如下所示时:

        if (adj_node, key) not in edges:
            edge = (key, adj_node)
            edges.add(edge)

那么当第一次遇到边a <-> b 时,它是(key, adj_node) == (a, b) 并被添加到集合中。当它遇到第二次(也是唯一一次)时,它是 (key, adj_node) == (b, a),这意味着 (adj_node, key) == (a, b) 已经在集合中,因此 (adj_node, key) not in edges 为 false 并且 (b, a) 不会被添加到集合中。

【讨论】:

  • 看起来我把元组和集合搞混了!感谢您指出了这一点!我花了很长时间才理解你对 if 语句的解释,但现在对我来说很有意义;当它检查“if (adj_node, key) not in edges”时,它仍然考虑之前由“edge = (key, adj_node)”语句存储的元组,这基本上是相同的。谢谢!
  • 平等测试和排序并不完全相同——例如在 Python 3.7 中,dicts 保留插入顺序,但 {1:1,2:2} == {2:2,1:1} 仍然是 TrueOrderedDict 不是这种情况,尽管 stackoverflow.com/questions/50872498/…)跨度>
猜你喜欢
  • 2022-12-02
  • 2019-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-09
  • 2018-08-16
  • 1970-01-01
  • 2021-09-18
相关资源
最近更新 更多