python-为什么这种o(n)三向设置不相交算法比o(n ^ 3)版本慢?
作者:互联网
O(n)因为将列表转换为集合是O(n)时间,所以得到交集是O(n)时间而len是O(n)
def disjoint3c(A, B, C):
"""Return True if there is no element common to all three lists."""
return len(set(A) & set(B) & set(C)) == 0
或类似地,应明确为O(N)
def set_disjoint_medium (a, b, c):
a, b, c = set(a), set(b), set(c)
for elem in a:
if elem in b and elem in c:
return False
return True
但是这个O(n ^ 3)代码:
def set_disjoint_slowest (a, b, c):
for e1 in a:
for e2 in b:
for e3 in c:
if e1 == e2 == e3:
return False
return True
运行更快
看到时间,其中算法一是n ^ 3,算法三是O(n)设置代码…算法二实际上是n ^ 2,我们在第三循环开始之前通过检查不相交性来优化算法一.
Size Input (n): 10000
Algorithm One: 0.014993906021118164
Algorithm Two: 0.013481855392456055
Algorithm Three: 0.01955580711364746
Size Input (n): 100000
Algorithm One: 0.15916991233825684
Algorithm Two: 0.1279449462890625
Algorithm Three: 0.18677806854248047
Size Input (n): 1000000
Algorithm One: 1.581618070602417
Algorithm Two: 1.146049976348877
Algorithm Three: 1.8179030418395996
解决方法:
这些评论澄清了Big-Oh表示法.因此,我将从测试代码开始.
这是我用于测试代码速度的设置.
import random
# Collapsed these because already known
def disjoint3c(A, B, C):
def set_disjoint_medium (a, b, c):
def set_disjoint_slowest (a, b, c):
a = [random.randrange(100) for i in xrange(10000)]
b = [random.randrange(100) for i in xrange(10000)]
c = [random.randrange(100) for i in xrange(10000)]
# Ran timeit.
# Results with timeit module.
1-) 0.00635750419422
2-) 0.0061145967287
3-) 0.0487953200969
现在来看结果,如您所见,O(n ^ 3)解决方案的运行速度比其他解决方案慢8倍.但这对于这样的算法仍然是快速的(在您的测试中甚至更快).为什么会这样呢?
因为您使用的是中速和慢速解决方案,所以一旦检测到公共元素,便完成了代码的执行.因此无法实现代码的全部复杂性.找到答案后便会中断.为什么最慢的解决方案在测试中的运行速度几乎与其他解决方案一样快?可能是因为它找到的答案更接近列表的开头.
要对此进行测试,您可以像这样创建列表.自己尝试一下.
a = range(1000)
b = range(1000, 2000)
c = range(2000, 3000)
现在,时间之间的真正差异将显而易见,因为最慢的解决方案必须运行直到完成所有迭代为止,因为没有公共元素.
因此,这是最坏情况和最佳情况性能的情况.
不是问题编辑的一部分:因此,如果您想保持发现早期常见事件的速度,又不想增加复杂性怎么办?为此,我提出了一个粗略的解决方案,也许更有经验的用户可以建议更快的代码.
def mysol(a, b, c):
store = [set(), set(), set()]
# zip_longest for Python3, not izip_longest.
for i, j, k in itertools.izip_longest(a, b, c):
if i: store[0].add(i)
if j: store[1].add(j)
if k: store[2].add(k)
if (i in store[1] and i in store[2]) or (j in store[0] and i in store[2]) or (k in store[0] and i in store[1]):
return False
return True
此代码基本上完成的工作是,避免在开始时将所有列表转换为集合.而是同时遍历所有列表,将元素添加到集合中,检查常见的情况.因此,现在,您可以保持找到早期解决方案的速度,但是对于我展示的最坏情况,它仍然很慢.
对于速度,在最坏的情况下,这比前两个解决方案慢3-4倍.但是运行速度比随机列表中的解决方案快4到10倍.
注意:毫无疑问,您将在三个列表中(第一个解决方案中)找到所有常见元素,这意味着从理论上讲,有一个更快的解决方案.因为您只需要知道是否只有一个公共元素,并且该知识就足够了.
标签:complexity-theory,python,algorithm 来源: https://codeday.me/bug/20191026/1937762.html