我试图确定这个函数的复杂性,其中D和元素是整数,list是整数的有序列表。请注意,(otherElement-element)将严格为正。
def doFunc(element, D, list):
x = 0
if(D > 0):
for otherElement in list:
if otherElement == element:
x += 1
if otherElement > element:
return x + (doFunc (otherElement,D-(otherElement-element) , list))
return x
鉴于列表不会总是完全迭代,我不知道如何确定此函数的时间复杂度。
答案 0 :(得分:1)
doFunc
从左到右检查list
,找到大于或等于提供的otherElement
的{{1}}。最多只进行一次递归调用。我们可以通过推导出最坏情况的输入和分析行为来尝试找出这个函数的最坏情况时间复杂度。
假设我们从大小为1的列表开始;称之为element
。如果我们在这个列表中调用函数,那么我们可以从{1}
循环中获得的迭代次数最多?好吧,如果我们设置for
,我们会得到一次迭代。但是,如果设置为element = 1
,我们可以element = 0
以doFunc
递归调用自身;这意味着我们得到两次迭代。说服自己,我们无法为此列表获得超过两次迭代element = 1
。也让自己相信doFunc
的选择本质上并不重要;任何单元素列表都可以以相同的方式工作。
现在假设我们想找到长度为2的最坏情况列表;下一个数字应该相同,更大还是更小?考虑{1}
,{1, 1}
和{1, 2}
。使用{1, 0}
调用doFunc
将分别导致element = -1
循环的最多3次,5次和3次迭代。添加更大的元素会导致长度为2的列表出现最糟糕的行为。
说服自己,最糟糕的情况是数字上升列表;实际上,由于for
的限制因素,最坏情况是D
个{a, a+1, a+2, ..., a+n-1}
元素的格式列表。对于此类列表,我们在设置n
时会出现以下行为:
element < a
的初始调用进行一次迭代;然后我们有doFunc
,所以我们递归调用otherElement > element
。doFunc
的第一次递归调用的两次迭代;然后我们有doFunc
,所以我们再次递归调用。otherElement > element
的{{1}}递归调用中,我们应该期望k
循环的doFunc
次迭代。由于k+1
循环在单个调用的上下文中迭代次数超过for
次,这意味着我们最多for
次递归调用n
我们有n - 1
。这是假设doFunc
。假设1 + 2 + ... + n = O(n^2)
,我们无法获得所有递归调用;在这种情况下,我们最多只能进行d > n
次迭代,或d < n
。因此,此函数的最坏情况行为是1 + 2 + ... + d
。另一个问题的复杂性是O(d^2)
,这比这里的复杂性要差,除非O(min(n^2, d^2))
,在这种情况下性能是相同的。
编辑:还要注意,这里时间复杂度的常数几乎可以保证比你的其他尝试要好得多,所以尽管具有相同的渐近复杂度,你仍然可以看到明显更好的性能。