算法效率的度量方法
容易想到的方法是:把算法跑若干次,然后拿个计时器在旁边计时。这种方法被称为“事后诸葛亮”方法,也称为事后分析估算方法。
事前分析估算方法:在计算机程序比编写前,依据统计方法对算法进行估算。
通过总结,我们发现,一个高级语言编写程序在计算机上运行所消耗的时间取决于下列因素:
(1)算法采用的策略,方案;
(2)编译产生的代码质量;
(3)问题的输入规模;
(4)机器执行指令的速度;
由上,抛开计算机硬件、软件因素,一个程序运行时间依赖于算法的好坏和问题输入的规模;
高斯求和算法:1+2+...+n的问题
算法1
int i, sum = 0, n = 100; // 执行1次
for( i=1; i <= n; i++ ) // 执行了n+1次
{
sum = sum + i; // 执行n次
}
算法2:
int sum = 0, n = 100; // 执行1次
sum = (1+n)*n/2; // 执行1次
如果我们把循环看做一个整体,忽略头尾判断的开销,那么这两个算法其实就是n和1的差距;
函数的渐近增长
假设两个算法的输入规模都是n,算法A要做2n+3次操作,你可以这么理解:先执行n次的循环,执行完成后再有一个n次的循环,最后有3次运算。
算法B要做3n+1次操作,理解同上,你觉得它们哪一个更快些呢?
规模 |
算法A1(2n+3) |
算法A2(2n) |
算法B1(3n+1) |
算法B2(3n) |
n=1 |
5 |
2 |
4 |
3 |
n=2 |
7 |
4 |
7 |
6 |
n=3 |
9 |
6 |
10 |
9 |
n=10 |
23 |
20 |
31 |
30 |
n=100 |
203 |
200 |
301 |
300 |
函数的渐近增长:
函数的渐近增长:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐近快于g(n);
第二个测试,算法C是4n+8,算法D是2n^2+1
次数 |
算法C1(4n+8) |
算法C2(n) |
算法D1(2n^2+1) |
算法D2(n^2) |
n=1 |
12 |
1 |
3 |
1 |
n=2 |
16 |
2 |
9 |
4 |
n=3 |
20 |
3 |
19 |
9 |
n=10 |
48 |
10 |
201 |
100 |
n=100 |
408 |
100 |
20001 |
10000 |
n=1000 |
4008 |
1000 |
2000001 |
1000000 |
算法的时间复杂度
定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n)=0(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。
关键:执行次数==时间
推导大O阶方法
如何分析一个算法的复杂度呢?如何推导大O阶呢?有以下攻略:
(1)用常数1取代运行时间中的所有加法常数;
(2)在修改后的运行次数函数中,只保留最高项;
(3)如果最高项存在且不为1,则去除与这个项相乘的常数;
常见的时间复杂度:
例子 |
时间复杂度 |
装逼术语 |
5201314 |
O(1) |
常数阶 |
3n+4 |
O(n) |
线性阶 |
3n^2+4n+5 |
O(n^2) |
平方阶 |
3log(2)n+4 |
O(logn) |
对数阶 |
2n+3nlog(2)n+14 |
O(nlogn) |
nlogn阶 |
n^3+2n^2+4n+6 |
O(n^3) |
立方阶 |
2^n |
O(2^n) |
指数阶 |
有图有真相:
常用的时间复杂度所耗费的时间从小到大依次是:O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
算法的空间复杂度
算法的空间复杂度通过计算算法所需的存储空间实现,算法的空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。
通常,我们都是用“时间复杂度”来指运行时间的需求,是用“空间复杂度”指空间需求。
当直接要让我们求“复杂度”时,通常指的是时间复杂度。
显然对时间复杂度的追求更是属于算法的潮流
★ finished by songpl 2018.11.24