【问题标题】:Python OOP Disjoint Set PerformancePython OOP 不相交集性能
【发布时间】:2017-09-24 04:47:08
【问题描述】:

我构建了一个与 Kruskal 的 MST 算法一起使用的不相交集数据结构。我需要加载并合并一个具有 200k 互连节点的图,我认为我的数据结构实现是一个瓶颈。

您对如何提高性能有什么建议吗?我认为我的 find 方法可能有问题。

class partition(object):
    def __init__(self, element=None):
        self.size = 0
        if element == None:
            self.contents = set()
            self.representative = None
        else:
            self.contents = {element}
            self.representative = element
            self.size = 1

    def find(self, element):
        return element in self.contents

    def add(self, partition):
        self.contents = self.contents.union(partition)
        self.size = len(self.contents)

    def show(self):
        return self.contents

    def __repr__(self):
        return str(self.contents)

class disjoint_set(object):
    def __init__(self):
        self.partitions_count = 0
        self.forest = {}

    def make_set(self, element):
        if self.find(element) == False:
            new_partition = partition(element)
            self.forest[new_partition.representative] = new_partition
            self.partitions_count += 1

    def union(self, x, y):
        if x != y:
            if self.forest[x].size < self.forest[y].size:
                self.forest[y].add(self.forest[x].show())
                self.delete(x)
            else:
                self.forest[x].add(self.forest[y].show())
                self.delete(y)

    def find(self, element):
        for partition in self.forest.keys():
            if self.forest[partition].find(element):
                return self.forest[partition].representative
        return False

    def delete(self, partition):
        del self.forest[partition]
        self.partitions_count -= 1

if __name__ == '__main__':
    t = disjoint_set()
    t.make_set(1)
    t.make_set(2)
    t.make_set(3)
    print("Create 3 singleton partitions:")
    print(t.partitions_count)
    print(t.forest)
    print("Union two into a single partition:")
    t.union(1,2)
    print(t.forest)
    print(t.partitions_count)

编辑:

阅读 cmets 并进行额外研究后,我意识到我的原始算法设计得多么糟糕。我从头开始,把它放在一起。我将所有分区放入一个哈希表中,并在 find() 中使用了路径压缩。这看起来如何?我应该解决什么明显的问题?

class disjoint_set(object):
def __init__(self):
    self.partitions_count = 0
    self.size = {}
    self.parent = {}

def make_set(self, element):
    if self.find(element) == False:
        self.parent[element] = element
        self.size[element] = 1
        self.partitions_count += 1

def union(self, x, y):
    xParent = self.find(x)
    yParent = self.find(y)
    if xParent != yParent:
        if self.size[xParent] < self.size[yParent]:
            self.parent[xParent] = yParent
            self.size[yParent] += self.size[xParent]
            self.partitions_count -= 1
        else:
            self.parent[yParent] = xParent
            self.size[xParent] += self.size[yParent]
            self.partitions_count -= 1

def find(self, element):
    if element in self.parent:
        if element == self.parent[element]:
            return element
        root = self.parent[element]
        while self.parent[root] != root:
            root = self.find(self.parent[root])
        self.parent[element] = root
        return root
    return False

if __name__ == '__main__':
    t = disjoint_set()
    t.make_set(1)
    t.make_set(2)
    t.make_set(3)
    t.make_set(4)
    t.make_set(5)
    print("Create 5 singleton partitions")
    print(t.partitions_count)
    print("Union two singletons into a single partition")
    t.union(1,2)
    print("Union three singletones into a single partition")
    t.union(3,4)
    t.union(5,4)
    print("Union a single partition")
    t.union(2,4)
    print("Parent List: %s" % t.parent)
    print("Partition Count: %s" % t.partitions_count)
    print("Parent of element 2: %s" % t.find(2))
    print("Parent List: %s" % t.parent)

【问题讨论】:

  • 你能添加一个 if name == 'main' 部分来显示用法吗?
  • 是的,很抱歉!现在添加。
  • Use a real disjoint-set forest data structure. 您刚刚选择了一些听起来有点像不相交集合森林的名称,然后编写了一个与真正的不相交集合森林没有任何关系的非常简单的算法。
  • 试试树形数据结构?

标签: python algorithm performance kruskals-algorithm disjoint-sets


【解决方案1】:

我猜你的 find 实现没有有效地运行,它应该是。

以下更改可能会有所帮助。

class disjoint_set(object):
    def __init__(self):
        self.partitions_count = 0
        self.forest = {}
        self.parent = {}

    def make_set(self, element):
        if not self.find(element):
            new_partition = partition(element)
            self.parent[element] = element
            self.forest[new_partition.representative] = new_partition
            self.partitions_count += 1

def union(self, x, y):
    if x != y:
        if self.forest[x].size < self.forest[y].size:
            self.forest[y].add(self.forest[x].show())
            #Update parent details 
            self.parent[self.forest[x].representative] = self.forest[y].representative
            self.delete(x)
        else:
            self.forest[x].add(self.forest[y].show())
            #Update parent details 
            self.parent[self.forest[y].representative] = self.forest[x].representative
            self.delete(y)

def find(self, element):
    if self.parent[element] == element:
        return element
    else:
        return find(element)

代码仍然可以通过路径压缩进行优化,以使 disjoint_set.find 在 O(1) 中运行。我猜 O(log n) 仍然适合大数字。

另一个瓶颈可能是你的联合函数。尤其是add函数的实现。

def add(self, partition):
    self.contents = self.contents.union(partition)

尝试使用 set 的更新方法(这是一个就地联合)。我认为这会导致大量节点的大量内存开销。类似的东西

self.contents.update(partition)

关于集合并集和更新函数here 有一个很好的讨论。

希望对你有帮助!

【讨论】:

  • 这根本不是路径压缩。该算法本质上与不相交集森林或路径压缩无关。
  • 路径压缩是指将 find 的执行保持在 O(1) 中。同意您所说的-这不是路径压缩实现。路径压缩的目的是保持 O(1) 中的操作对吗?这就是我想传达的一些东西。删除了对路径压缩的提及。谢谢指点。
  • 也不是 O(1),路径压缩不会使 find 在 O(1) 中运行。这里的partition.find 方法是O(1),但这不是标准的不相交集操作;实际的不相交集查找操作由disjoint_set.find 实现,我们必须实际找到元素属于哪个集合。那是 O(len(self.forest)),太糟糕了。
  • 同意。 disjoint_set.find 的实现存在缺陷。我做了一些改变。 @user2357112 检查这是否可以改进或任何其他缺陷。
  • 感谢 @ArunKumar 的 cmets。我会尝试这些更改。
猜你喜欢
  • 1970-01-01
  • 2019-05-31
  • 2010-10-29
  • 2016-10-17
  • 2013-10-04
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多