您好我需要了解以下算法的时间复杂度。
def complex(n):
l=[]
i=1
while i<n:
l=list(range(i))
i*=2
我已经意识到它在循环中运行int(log(n,2))次,但我很难将范围(i)合并到最终表达式中。 任何帮助表示感谢。
答案 0 :(得分:3)
您已经知道它运行int(log(n, 2))
次迭代。 (您可以通过在循环中添加一个计数器并使用例如1,2,4,8,16,32,64等调用它来非常轻松地进行测试,并且每次都看到计数器上升1 n
加倍。)
现在你想知道循环内部需要多长时间。在这里,您需要知道range
和list
函数的时间复杂度。我可以给你那些答案,事实上你可能会猜到它们,但除非你开始阅读CPython的源代码,否则你无法真正证明。所以,让我们用一些简单的时间进行测试:
import timeit
for i in range(20):
n = 1 << i
t = timeit.timeit(lambda: list(range(n))
print('{} takes {}'.format(n, t))
如果你运行它,你会看到,一旦你超过32,加倍n
似乎加倍了它所需的时间。那么,这意味着list(range(n))
是O(n),对吗?
让我们验证一下是否合理。我不知道你是使用Python 2.x还是3.x,所以我会用两种方式解决。
在2.x中:range(n)
必须计算n
个整数,并构建一个列表n
值。这似乎应该是O(n)。
在3.x中:range(n)
只返回一个记住数字n
的对象。那应该是O(1)。但是我们会在list
上调用range
,它必须迭代整个范围,计算所有n
个整数,并构建长n
个列的值。所以它仍然是O(n)。
将它放回到循环中,循环中有O(log n)次,每次O(i)复杂度。因此,总时间为O(1)+ O(2)+ O(4)+ O(...)+ O(n / 4)+ O(n / 2)+ O(n),其中log(n)总结中的步骤。换句话说,它是geometric sequence的总和。现在你可以解决问题了。 (或者,如果没有,你就会陷入一个新的角色,如果你自己无法解决这个问题,有人可以为你解决这个问题。)
你得出结论是-(1-2**log(n,2))
。这不太对,因为你想要一个封闭的范围,而不是半开范围,所以它应该是-(1-2**log(n+1,2))
。但这可能是我没有清楚地解释它的错,而且没关系太多,所以让我们首先考虑你的版本。
2**log(n, 2)
显然是n
。 (如果你不能很好地理解取幂和对数,你应该找到一个关于数学的教程,但同时你可以用各种不同的n
值来测试它,以说服自己。)
同时,任何-(1-x)
的{{1}}只是x
。
所以,总和只是x-1
。
如果您返回并使用正确的n-1
代替log(n+1, 2)
,则会获得log(n, 2)
。
那么,这是正确的吗?让我们测试一些实际数字。
如果2n-1
,您获得n = 16
。如果1+2+4+8+16 = 31 = 2n-1
,则获得n = 1024
。你投掷的任何2的幂,你得到了正确的答案。对于非幂2,如1000,你会得到1+2+4+…+256+512+1024 = 2047 = 2n-1
,这不完全是1+2+4+…+256+512+1000 = 2023
,但它总是在2的因子内。(事实上,它是2n-1
,或n + 2**(ceil(log(n, 2)) - 1
其中n + m - 1
为m
的四舍五入为2的幂。)
无论如何,n
,n-1
,2n-1
......这些都是n + 2**(ceil(log(n, 2)) - 1
。
你可以通过使用O(n)
的不同值对整个函数进行计时来回过头来测试这一点,并且看到,除了非常小的数字,当你加倍n
它需要大约两倍的时间。< / p>
答案 1 :(得分:0)
这个比它看起来更棘手,因为list(range(i))
基于i,它在几何上增长。但是,有一种简单的方法来查看问题。相对于n,总共创建了多少个元素?您可以通过假设n的方便值(即2的幂)来简化问题。
如果你仍然陷入困境,那就从小处开始吧。如果n = 1,总共创建了多少个元素?然后看看你得到的答案是n = 2,n = 4,n = 8,依此类推。模式应该很快就会变得明显。