我正在尝试教自己如何为任意函数计算BigO表示法。我在教科书中找到了此功能。该书断言该函数为O(n 2)。它解释了为什么这样做,但是我一直在努力遵循。我想知道是否有人能够向我展示为什么会这样。从根本上讲,我知道它小于O(n 3),但我无法独立降落在O(n 2)上
假设给定了三个数字序列,A,B和C。我们将假定没有单个序列包含重复值,但是在两个或三个序列中可能存在一些数字。三向不相交问题是确定三个序列的交点是否为空,即是否不存在元素x使得x∈A,x∈B和x∈C。
顺便说一句,这对我来说不是一个作业问题,因为这艘船几年前已经航行过:),只是我想变得更聪明。
def disjoint(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
if a == b: # only check C if we found match from A and B
for c in C:
if a == c # (and thus a == b == c)
return False # we found a common value
return True # if we reach this, sets are disjoint
[编辑]根据教科书:
在改进的版本中,如果幸运的话,不只是节省时间。我们声称不相交的最坏情况下的运行时间为O(n 2)。
我难以理解的这本书的解释是:
为了说明整体运行时间,我们检查了执行每一行代码所花费的时间。在A上进行for循环的管理需要O(n)时间。B上的for循环的管理总共需要O(n 2)时间,因为该循环执行了n次不同的时间。测试a == b被评估O(n 2)次。剩下的时间取决于存在多少对匹配的(a,b)。正如我们已经指出的,最多有n个这样的对,因此对C的循环的管理以及该循环体内的命令最多使用O(n 2)时间。花费的总时间为O(n 2)。
(并给予应有的荣誉……)这本书是:Michael T. Goodrich等人的《 Python中的数据结构和算法》。全部,Wiley Publishing,第16页。135
理由 下面是优化前的代码:
def disjoint1(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
for c in C:
if a == b == c:
return False # we found a common value
return True # if we reach this, sets are disjoint
在上面,您可以清楚地看到这是O(n 3),因为每个循环必须充分运行。这本书会断言,在简化的示例中(首先给出),第三个循环只是O(n 2)的复杂度,因此复杂度方程为k + O(n 2)+ O(n 2),最终得出O(n 2)。
虽然我不能证明是这种情况(因此是问题),但读者可以同意简化算法的复杂度至少要比原始算法少。
[编辑]并证明简化版本是二次的:
if __name__ == '__main__':
for c in [100, 200, 300, 400, 500]:
l1, l2, l3 = get_random(c), get_random(c), get_random(c)
start = time.time()
disjoint1(l1, l2, l3)
print(time.time() - start)
start = time.time()
disjoint2(l1, l2, l3)
print(time.time() - start)
产量:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
由于第二个差相等,因此简化函数的确是二次的:
[编辑]还有进一步的证明:
如果我假设最坏的情况(A = B!= C),
if __name__ == '__main__':
for c in [10, 20, 30, 40, 50]:
l1, l2, l3 = range(0, c), range(0,c), range(5*c, 6*c)
its1 = disjoint1(l1, l2, l3)
its2 = disjoint2(l1, l2, l3)
print(f"iterations1 = {its1}")
print(f"iterations2 = {its2}")
disjoint2(l1, l2, l3)
产量:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
使用第二次差异测试,最坏情况的结果恰好是二次的。