测试加权快速联合算法?

时间:2015-07-02 14:05:45

标签: python algorithm

我正在通过Coursera学习Algorithms, Part I,并希望测试快速查找,快速联盟和加权快速联盟算法的运行时间。这门课程是用Java编写的,我不熟悉,所以我已经完成并尝试在Python中重新创建算法,我更熟悉。

现在我已经完成了所有工作,我的目标是测试每个函数以验证运行时间/复杂性。我一直在考虑使用timeit库,但这似乎会导致不正确的结果,例如,加权快速联盟比QuickUnion需要更长的时间才能完成。

如何验证加权快速联盟实际上是O(log n)并且比Quick Union快?以下是我迄今为止创建和尝试的内容:

O(N ** 2) - 慢

class QuickFind_Eager:
    def __init__(self, nodes):
        self.array = [num for num in range(nodes)]

    # Joins two nodes into a component
    def union(self, first_node, second_node):
        for pos, val in enumerate(self.array):
            if self.array[pos] == self.array[first_node]:
                self.array[pos] = self.array[second_node]

    # Checks if two nodes are in the same component
    def connected(self, first_node, second_node):
        return self.array[first_node] == self.array[second_node]

O(N) - 仍然太慢 - 避免

class QuickUnion_Lazy:
    def __init__(self, nodes):
        self.array = [num for num in range(nodes)]

    # Follows parent pointers to actual root
    def root(self, parent):
        while parent != self.array[parent]:
            parent = self.array[parent]
        return parent

    # Joins two nodes into a component
    def union(self, first_node, second_node):
        self.array[first_node] = self.array[second_node]

    # Checks if two nodes are in the same component
    def connected(self, first_node, second_node):
        return self.root(first_node) == self.root(second_node)

O(log N) - 非常快速

class WeightedQuickUnion:
    def __init__(self, nodes):
        self.array = [num for num in range(nodes)]
        self.weight = [num for num in range(nodes)]

    # Follows parent pointers to actual root
    def root(self, parent):
        while parent != self.array[parent]:
            parent = self.array[parent]
        return parent

    # Joins two nodes into a component
    def union(self, first_node, second_node):
        if self.root(first_node) == self.root(second_node):
            return

        if (self.weight[first_node] < self.weight[second_node]):
            self.array[first_node] = self.root(second_node)
            self.weight[second_node] += self.weight[first_node]
        else:
            self.array[second_node] = self.root(first_node)
            self.weight[first_node] += self.weight[second_node]

    # Checks if two nodes are in the same component
    def connected(self, first_node, second_node):
        return self.root(first_node) == self.root(second_node)

O(N + M lg * N) - 邪恶

class WeightedQuickUnion_PathCompression:
    def __init__(self, nodes):
        self.array = [num for num in range(nodes)]
        self.weight = [num for num in range(nodes)]

    # Follows parent pointers to actual root
    def root(self, parent):
        while parent != self.array[parent]:
            self.array[parent] = self.array[self.array[parent]]
            parent = self.array[parent]
        return parent

    # Joins two nodes into a component
    def union(self, first_node, second_node):
        if self.root(first_node) == self.root(second_node):
            return

        if self.weight[first_node] < self.weight[second_node]:
            self.array[first_node] = self.root(second_node)
            self.weight[second_node] += self.weight[first_node]
        else:
            self.array[second_node] = self.root(first_node)
            self.weight[first_node] += self.weight[second_node]

    # Checks if two nodes are in the same component
    def connected(self, first_node, second_node):
        return self.root(first_node) == self.root(second_node)

测试运行时间

def test_quickfind(quickfind):
    t = quickfind(100)
    t.union(1,2)
    t.connected(1,2)
    t.union(4,2)
    t.union(3,4)
    t.connected(0,2)
    t.connected(1,4)
    t.union(0,3)
    t.connected(0,4)

import timeit

t = timeit.timeit(stmt="test_quickfind(QuickFind_Eager)", setup="from __main__ import QuickFind_Eager; from __main__ import test_quickfind", number=100000)
print(t)
# 11.4380569069981
t = timeit.timeit(stmt="test_quickfind(QuickUnion_Lazy)", setup="from __main__ import QuickUnion_Lazy; from __main__ import test_quickfind", number=100000)
print(t)
# 1.4744456350017572
t = timeit.timeit(stmt="test_quickfind(WeightedQuickUnion)", setup="from __main__ import WeightedQuickUnion; from __main__ import test_quickfind", number=100000)
print(t)
# 2.738758583996969
t = timeit.timeit(stmt="test_quickfind(WeightedQuickUnion_PathCompression)", setup="from __main__ import WeightedQuickUnion_PathCompression; from __main__ import test_quickfind", number=100000)
print(t)
# 3.0113827050008695

更新 添加了timeit的结果。

2 个答案:

答案 0 :(得分:2)

您需要将算法制成表格&#39;运行时间作为问题大小的函数,即。针对不同的问题规模调用quickfind(比如100,200,300,400,500;请注意,期望后者至少运行3分钟的天真O(n^2)算法。)

您仍然无法保证您观察到渐近运行时函数(O符号的含义:O(f)实际上描述了一系列函数g_ig_i = a_i * f(n) + b_i; a_i, b_i: const [有点滥用符号]),因为你的某些实现可能会遇到资源耗尽(读取:不再使用ram),导致性能超出实现范围。

答案 1 :(得分:0)

在QuickFindEager类中实现并集函数不正确。 self.array[first_node]self.array[second_node]应该在循环之前添加到变量中,而不是在变量中循环更改

def union(self, first_node, second_node):
    pid = self.array[first_node]
    qid = self.array[second_node]
    for pos, val in enumerate(self.array):
        if self.array[pos] == pid:
            self.array[pos] = qid