评估算法的时间复杂度(time complexity)的技巧小结
评估算法的时间复杂度的技巧小结
这篇文章献给澳门理工学院一起努力的同学们,祝大家早日摆脱算法学习的苦海,找到一叶扁舟。
什么是时间复杂度
众所周知,程序运行的时间长短跟硬件和算法都有关系。当人们想要专注于研究算法的优劣时,就要在某种程度上排除硬件差异带来的评估干扰,这时时间复杂度的概念就被发明出来了。
时间复杂度(time complexity),是评估算法好坏的一个指标,关于它的本质,简单概括就是:时间复杂度是一个算法的输入和它运行所需的时间之间的函数特征。
我们把这个输入称之为问题规模,而这个函数特征,并不是一个完全的函数,而是能够表现关系特征的一种函数类型。以下列出了最常见的函数类型,在时间复杂度中,它们表示的效率由高到低:
| 函数类型 | 表示 |
|---|---|
| Constant | 1 |
| Logarithmic | logn |
| Linear | n |
| Linearithmic | nlogn |
| Quadratic | n2 |
| Cubic | n3 |
| Exponential | 2n |
时间复杂度可以写作O( )形式,如O( n ), O( nlogn )
朴素方法分析时间复杂度
最朴素的分析时间复杂度的方法就是计算基础操作次数,算出运行次数关于问题规模的函数,提取出特征并得出结论
基础操作大抵包括调用,判断,赋值,读值,等等,其中读值一般特指从列表等数据结构中读取。其他在此不详细讲述了,实际操作中往往不需要使用这种分析方法。
例子:
def list_max(a,n): # 基础操作次数m = a[0] 2 【赋值,读值】i = 1 1while i < n: 1*n 【n是输入的**问题规模** 】if a[i] > m: 2*(n-1) 【判断,读值】m = a[i] 2*(n−1)i = i +1 2*(n−1)return m 1total:7n−2
提取特征可得时间复杂度为O(n)
可以发现,所谓提取特征,就是提取原函数中最高次幂的项,去掉系数。
实际操作技巧
时间复杂度的分析主要依靠对循环(loop)的分析
首先需要明确问题规模,问题规模往往是输入的一个上限(下限),或者是输入的一组数据的大小(个数)。
一般情况下:
1, 如果算法中无循环,则时间复杂度是常数O(1)。
常数时间复杂度的意思是算法的运行时间和算法的输入完全没有关联。
2,如果存在一个或多个并列的循环:
如果循环变量的上限下限与问题规模无关,如for(i = 0;i <= 5; i++),时间复杂度是O(1)
如果循环变量都以乘法递增,如for(i = 0;i <= n; i*=2),则时间复杂度是O(logn)
如果存在循环变量以加法递增,如for(i = 0;i <= n; i++),则时间复杂度是O(n)
3,如果存在嵌套的循环:
在每一个嵌套体上,根据第2条的判断方式判断出每一层循环的时间复杂度,将它们乘起来即可。如以下代码:
for(i = 0;i <= n; i*=2){for(j = 0;j <= n; j++){... ...
时间复杂度是O(nlogn)
对于一个完整的算法,按照上面的规则计算每一个代码块,根据并列相加,嵌套相乘的原则得到一个多项式,取最大次幂项特征作为时间复杂度即可。
更加复杂的情况
时间复杂度求解的过程往往会遇到更多复杂的问题,这就需要利用数学技巧计算或证明了。例如以下代码:
for(i = 0;i <= n; i*=i){for(j = 0;j <= i; j++){... ...
问题规模是n,就需要用到数列求和公式等数学技巧,或者是代码等价转换的思想来尝试求解时间复杂度了。
另一个比较复杂的问题是时间复杂度的最好情况和最坏情况,其产生的原因是:对于一个算法,除了问题规模之外,还存在其他的输入影响其运行的时间。比如,排序算法中,问题规模是排序数组的长度,但是数组内元素可能比较离散,也可能存在大量重复,这就导致排序算法排序相同长度的数组可能花费不同的时间,时间复杂度就会存在波动,这种波动往往也是一个函数。
例子:Python查找链表中倒数第 i 个节点:
def back_node(self,i):p=self.headwhile True:q = pfor j in range(i):if not q:return Noneq=q.nxtif not q:return pp = p.nxt
这其中,问题规模是链表长度n,但是输入的index值i也会影响算法运行的时间。
先不管i,朴素分析可得到时间复杂度原式: i ∗ ( n − i + 1 ) i*(n - i + 1) i∗(n−i+1)
我们将 i 表示为: i = n / x i = n / x i=n/x带入原式中,计算得到: T i m e C o m p l e x i t y = ( x ∗ n 2 + x ∗ n − n 2 ) / x 2 TimeComplexity = (x*n^2 + x*n - n^2) / x^2 TimeComplexity=(x∗n2+x∗n−n2)/x2
n固定时,当 x 取 2,也就是: i = ( n + 1 ) / / 2 i = (n + 1) / / 2 i=(n+1)//2所需运行时间最长。x取不同值时,函数图像大致如下:
结论:本算法的最好时间复杂度是O(n),比如 i 取 1。最坏时间复杂度是O(n2),i 取 (n + 1) / / 2。
大多数情况下,只需要分析当问题规模固定时,其他输入影响运行时间变化的最极端情况即可。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
