我试图找到函数的时间复杂度。
评估算法每个步骤的复杂程度以及整个函数的复杂性。
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(n)
,使第3行for
循环的复杂度为O(n·n) = O(n²)
。因此,对于包含O(n²)
个简单元素的列表,整个函数将为n
。
现在,我们假设我们列出了n
个列表,每个列表都包含n
个元素。根据之前的结果,我知道第5行是O(n³)
,因为它是嵌套在O(n²)
中的O(n)
。第3行将是O(n⁴)
,因为第5行执行n
次,这使得此案例的整体复杂度O(n⁴)
。
对于n
个n
列表的列表,每个列表都包含n
元素,第5行将是O(n⁵)
,因此整个循环和函数将是O(n⁶)
。
我很清楚,复杂性不仅取决于列表的长度,而且还因为缺少更好的词,列表的深度。
我想说这个函数的复杂度为O(n^(2·k))
,其中k
是深度
对于一个简单的列表,k = 1
;获取简单列表列表k = 2
;等
这种分析是否正确?如果没有,它有什么问题,答案是什么?
答案 0 :(得分:2)
你对n
的错误(又名“无用”)定义有点误导。此外,您似乎认为append
已摊销O(n)
。它不是;它已摊销O(1)
。
考虑在列表T(l)
上花费时间l
的操作。
new=[]
for e in ls:
if type(e) == list:
new.append(deep_copy(e))
else:
new.append(e)
return new
是
O(1) + [O(1) assign]
O(len(l)) * ( [len(l) loops + O(1) overhead for each loop]
(O(1) + [O(1) if]
O(1) + T(lᵢ)) or [O(1) append and T(lᵢ) recursion]
(O(1) + [O(1) else]
O(1)) [O(1) append]
) + O(1) [O(1) return]
只是
T(l) = O(len(l)) * [T(lᵢ) or O(1)] + O(1)
请注意,因为T(lᵢ) or O(1)
依赖两者 lᵢ
和i
上的,我们无法解决这种递归就像正常一样。
我们的递归适用于非正方形甚至非矩形数组。这意味着我们不能仅在n
中使用n
列表的长度对其进行参数化。
相反,我们可以以不同的数量对其进行参数化。
我们知道递归将总共遍历N
个元素。这意味着我们会有像
[
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)] +
[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(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ⁱ⁺¹)
对于具有(n, n, n, ..., n)
维度的维度i
来说并不完全正确。它应该是O(nⁱ + i)
。
现在,我确实做了很奇怪的事。我没有写O(nⁱ + i)
= O(nⁱ)
。
当你有两个变量时,通常(但并非总是)值得考虑只有一个的情况。通常为O(nⁱ) > O(i)
,但如果您可以有n
和i
,那么let n = 1
和O(nⁱ) < O(i)
。
基本上,O(nⁱ + i) = O(nⁱ)
当且仅当您想要时才会这样做。如果案例n = 1
,i large
很重要,请添加+ i
。否则,不要。
我们可以通过设置i
和时间来快速测试功率为i+1
,而不是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 loops, best of 3: 20.8 msec per loop
10 loops, best of 3: 209 msec per loop
如我所说,当长度增加10倍所花费的时间增加10倍意味着线性成本。
答案 1 :(得分:1)
查看带有行号的功能以供参考。另外,感谢@Veedrac指出append在Python中是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)*(循环迭代的大哦)。所以问题是第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表示元素总数是有用的是正确的。
答案 2 :(得分:0)
另一方面,假设你改为
def deep_copy(ls):
new=[]
for e in ls:
if type(e) == list:
new = new + [deep_copy(e)]
else:
new = new + [e]
return new
new = new + [...]
O(n)
。那实际上是时间的复杂性呢?
从我的其他答案中得出结果,
T(l) = O(len(l)) * [T(lᵢ) or O(1)] + O(1)
我们知道[]
内的每条路径的成本都有额外的O(len(l))
。
T(l) = O(len(l)) * [T(lᵢ) + O(len(l)) or O(1) + O(len(l))] + O(1)
= O(len(l)) * [(T(lᵢ) or O(1)) + O(len(l))] + O(1)
但是通过展开我们刚刚拥有的括号:
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))
因此对于具有(n, n, n, ..., n)
维度的维度i
,我们有
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
不出所料,这是Rob Watts在O(n)
追加时所拥有的。