Python 递归函数的求值&x27;时间复杂度 我想要什么
我试图找出函数的时间复杂度 评估算法每个步骤的复杂性以及整个函数的复杂性。Python 递归函数的求值&x27;时间复杂度 我想要什么,python,algorithm,list,time-complexity,Python,Algorithm,List,Time Complexity,我试图找出函数的时间复杂度 评估算法每个步骤的复杂性以及整个函数的复杂性。 1 def deep_copy(ls): 2 new=[] 3 for e in ls: 4 if type(e) == list: 5 new.append(deep_copy(e)) 6 else: 7 new.append(e) 8 return new 我试过的 我知道第2、4、6和8行都是O(1) 最好的
1 def deep_copy(ls):
2 new=[]
3 for e in ls:
4 if type(e) == list:
5 new.append(deep_copy(e))
6 else:
7 new.append(e)
8 return new
我试过的
- 我知道第2、4、6和8行都是
O(1)
- 最好的情况是复制的列表只有简单的元素(如果愿意,列表中没有列表)
如果是这种情况,那么第7行的最大复杂度为
,使得第3行O(n)
循环的复杂度为for
。因此,对于包含O(n·n)=O(n²)
简单元素的列表,整个函数将是n
O(n²)
- 现在,假设我们有一个
列表,每个列表都包含n
元素。根据前面的结果,我知道第5行是n
,因为它是嵌套在O(n³)
中的O(n)
。第3行将是O(n²)
O(n⁴)代码>因为第5行执行了
次,使得总体复杂性n次
O(n⁴)代码>用于此案例
- 对于
列表中的n
列表中的每个n
元素,第5行将是n
O(n⁵)那么整个循环和函数将是
O(n⁶)代码>
1 def deep_copy(ls):
2 new=[]
3 for e in ls:
4 if type(e) == list:
5 new.append(deep_copy(e))
6 else:
7 new.append(e)
8 return new
我想说这个函数的复杂性是O(n^(2·k))
,其中k
是深度。对于简单列表,
k=1
;对于简单列表列表,k=2
;等等
此分析正确吗?如果不正确,它有什么问题,正确答案是什么?查看带有行号的函数以供参考。另外,感谢@Veedrac指出,在Python中,append是O(1)
1 def deep_copy(ls):
2 new=[]
3 for e in ls:
4 if type(e) == list:
5 new.append(deep_copy(e))
6 else:
7 new.append(e)
8 return new
正如你所说,第2、4、6和8行都是O(1)。第7行是O(1),第3行是O(n)*(循环迭代的大Oh)。所以问题是第5行的时间复杂性
第5行大致相当于:
_ = deep_copy(e)
new.append(_)
这个函数的时间复杂度是多少?它是deep_copy(e)
的时间复杂度加上追加-O(1)的时间复杂度。如果e
不包含任何列表,第5行的时间复杂度将是O(n),导致整个函数为O(n^2)。因此,时间复杂度不是O(n^(2*k)),而是O(n^k)
然而,到目前为止,n已经被用来表示列表的长度。如果我们用n来表示所有列表中的元素总数会怎么样?我还没有研究并确定基于元素总数的时间复杂度,但它应该值得研究
编辑:
我更新了我的计算,以反映追加有O(1)。此外,@Veedrac表明,我认为有n意味着元素的总数将是有用的是正确的。你被
n
的一个糟糕(又称“不有用”)定义稍微误导了。此外,你似乎认为append
是摊销O(n)
。它不是;它是摊销的O(1)
考虑在列表l
上花费时间的操作T(l)
new=[]
for e in ls:
if type(e) == list:
new.append(deep_copy(e))
else:
new.append(e)
return new
是
这只是
T(l) = O(len(l)) * [T(lᵢ) or O(1)] + O(1)
[
[T(l₁₁) + T(l₁₂) + T(l₁₃) + T(l₁₄) + ... + O(1)] +
[T(l₂₁) + T(l₂₂) + T(l₂₃) + T(l₂₄) + ... + O(1)] +
[T(l₃₁) + T(l₃₂) + T(l₃₃) + T(l₃₄) + ... + O(1)] +
[T(l₄₁) + T(l₄₂) + T(l₄₃) + T(l₄₄) + ... + O(1)] +
...
] + O(1)
注意,因为T(lᵢ) 或O(1)
取决于l的类型ᵢ在i
上,我们不能像平常一样解决这个递归
我们已经知道,我们的递归可以在非正方形甚至非矩形数组上工作。这意味着我们不能只在n
中用n
列表的长度参数化它
相反,我们可以用不同的量来参数化它
我们知道递归将总共遍历N
个元素
[
T(l₁) +
T(l₂) +
T(l₃) +
T(l₄) +
...
] + O(1)
这只是
T(l) = O(len(l)) * [T(lᵢ) or O(1)] + O(1)
[
[T(l₁₁) + T(l₁₂) + T(l₁₃) + T(l₁₄) + ... + O(1)] +
[T(l₂₁) + T(l₂₂) + T(l₂₃) + T(l₂₄) + ... + O(1)] +
[T(l₃₁) + T(l₃₂) + T(l₃₃) + T(l₃₄) + ... + O(1)] +
[T(l₄₁) + T(l₄₂) + T(l₄₃) + T(l₄₄) + ... + O(1)] +
...
] + O(1)
那是
[
[T(l₁₁) + T(l₁₂) + T(l₁₃) + T(l₁₄) + ...] +
[T(l₂₁) + T(l₂₂) + T(l₂₃) + T(l₂₄) + ...] +
[T(l₃₁) + T(l₃₂) + T(l₃₃) + T(l₃₄) + ...] +
[T(l₄₁) + T(l₄₂) + T(l₄₃) + T(l₄₄) + ...] +
...
] + O(1) + T(len(l))
O(Σn^k from k=0 to k=i-1) · O(n²) +
= O(nⁱ⁺¹ + i) = O(nⁱ⁺¹) as i+1 ≥ 2
也就是说,递归地展开
[
[[[[...[O(1) + O(1) + ...]...]]]] +
[[[[...[...]...]]]] +
[[[[...[...]...]]]] +
[[[[...[...]...]]]] +
...
] + O(1) + O(len(l)) +
+ O(len(l₁)) + ... + O(len(l₄)) + ... +
+ O(len(l₁₁) + ... + O(len(l₄₄)) + ... +
+ O(len(l₁₁₁) + ...
+ ...
第一部分(在[]
s中)添加到O(N)
。第二部分是列表长度的总和
显然,列表的长度之和至少与基本级别项目的总数一样大
[[[...[[[[1]]]...]]]
需要很长时间才能复制,但只有一项
所以我们的答案是
O(sum(number of items in each list, including all sublists))
那么为什么不在n
或n
中进行参数化呢?为什么只是一个文本框
那是因为这就是你所需要的,也是你所能给予的。如果你有另一个定义,比如:
数组的维数为(x₁, x₂, x₃, ..., xₙ)代码>
然后你可以用变量(O(∑x+πx)
)来求解,但这不是问题给出的结果
注意O(nⁱ⁺1)
,正如前面所建议的,对于维度(n,n,n,…,n)
和i
维度的东西不太合适。它应该是O(nⁱ + i)
现在,我确实做了一件非常奇怪的事情。我没有写O(n)ⁱ + i)
=O(nⁱ)代码>
当你有两个变量时,通常(但不总是)值得考虑只有一个变量较大的情况ⁱ) > O(i)
,但是如果你可以有任何n
和i
,那么让n=1
和O(nⁱ) < O(i)
基本上,O(nⁱ + i) =O(n)ⁱ)
当且仅当您希望它时。如果案例n=1
,i large
很重要,请包含+i
。否则,请不要
通过设置i=1
和计时,我们可以快速测试电源是否为i
,而不是i+1
:
SETUP="
def deep_copy(ls):
new=[]
for e in ls:
if type(e) == list:
new.append(deep_copy(e))
else:
new.append(e)
return new
"
python -m timeit -s "$SETUP" -s "x = [0] * 10**5" "deep_copy(x)"
python -m timeit -s "$SETUP" -s "x = [0] * 10**6" "deep_copy(x)"
给予
正如我所说的,随着长度增加10,花费的时间增加了10,这意味着线性成本。另一方面,假设你有
def deep_copy(ls):
new=[]
for e in ls:
if type(e) == list:
new = new + [deep_copy(e)]
else:
new = new + [e]
return new
T(l) = O(len(l)) * [T(lᵢ) or O(1)] + O(len(l))·O(len(l)) + O(1)
= O(len(l)) * [T(lᵢ) or O(1)] + O(len(l)²)
O(sum(square of number of items in each list, including all sublists))
O(
n² + first level; one list of length n
n · n² + second level; n lists of length n
n · n · n² + third level; n² lists of length n
... +
nⁱ⁻¹ · n² "i"th level; nⁱ⁻¹ lists of length n
)
O(Σn^k from k=0 to k=i-1) · O(n²) +
= O(nⁱ⁺¹ + i) = O(nⁱ⁺¹) as i+1 ≥ 2