我的意思是,如果数组大小变大,时间复杂度可能会变大,对吗?而且总数会越来越大,不会影响复杂度吗?
伪代码:
def find_sum(given_array)
total = 0 # refers to O(1)
for each i in given array: #O(1)
total+=i #O(1)
return total #O(1)
def find_sum(given_array)
total = 0 # refers to O(1)
for each i in given array: #O(1)
total+=i #O(1)
return total #O(1)
def find_sum(given_array)
total = 0
for each i in given array:
total+=i
return total
O(n)
,因为随着输入的增加,复杂度呈线性而不是常数增长。更准确地说是Θ(n)
。def find_sum(given_array)
total = 0 # refers to O(1)
for each i in given array: #O(1)
total+=i #O(1)
return total #O(1)
def find_sum(given_array)
total = 0 # takes c1 time
for each i in given array:
total+=i # takes c2 time
return total # takes c3 time
c1 + n * c2 + c3
,可以简化为 n。由于该函数的下限和上限相同,因此我们可以使用 Θ(n)
代替 O(n)
。到目前为止,没有回答第二个问题:
总数也会变得越来越大,这不会影响复杂度吗?
这是非常重要的问题,通常被忽略。
答案取决于你的计算模型。如果底层机器可以在恒定时间内添加任意大的数字,那么不,它不会影响时间复杂度。
然而,现实中的机器是以固定宽度的值进行操作的。现代计算机可以愉快地添加64位数量。有些机器一次只能添加16位宽度的值。图灵机——整个复杂性理论的基础——每次只处理1位。无论如何,一旦我们的数字超过寄存器宽度,我们必须考虑加法所需的时间与加数的位数成正比,而在这种情况下,加数的位数是 log(i)
(或者 log(total)
,但由于 total
的增长速度为 i*(i-1)/2
,其位宽约为 log(i*i) = 2 log(i)
)。
有了这个想法,注释如下:
total+=i # O(log i)
更为谨慎。现在循环的复杂度
for each i in given array:
total+=i # O(log(i))
sum[1..n] log(i) = log(n!) ~ n log(n)
。最后一个等式来自于阶乘的 Stirling 近似。
这是一个有趣的见解,答案取决于您如何构思数字的表示方式。如果您有固定长度的数字表示(32位无符号整数、双精度浮点数等),那么加法是一个常数时间操作。如果您有可变长度的表示(例如大整数库或手动执行算法),则添加的复杂性取决于所使用的加法方法,但必然随着数字大小的增加而增加(对于常规的带进位加法,可能存在上限对数界)。实际上,对于可变长度的表示,您的复杂性至少应包括与数组中数字的大小(最大或平均值)相关的某些参数;否则,运行时可能会被添加数字而不是循环所主导(例如,两个1000^1000位整数的数组几乎会花费所有时间来添加而不是循环)。
这个循环没有办法:
for each i in given array:
total+=i
将在O(1)时间内运行。即使输入的大小n
为1,渐近分析仍然会表明它以O(n)而不是O(1)运行。
渐近分析测量时间/空间复杂度与输入大小的关系,并不一定显示执行的确切操作数。
指出O(1)是常数,并不意味着它只有一个(1)操作,而是意味着这个特定的块(它需要O(1))不随输入变化而改变,因此它具有恒定的复杂度。
另一方面,O(n)表示复杂度取决于n
,并且根据输入n
的变化而变化。当输入大小和运行时间具有1对1的相关性时,O(n)是线性关系。
正确编写的注释应该像这样:
def find_sum(given_array)
total = 0 #this is O(1), it doesn't depend on input
for each i in given array: #this is O(n), as loop will get longer as the input size gets longer
total+=i #again, O(1)
return total #again, O(1)