斐波那契数列引发的思考
第一种写法:
#include<bits/stdc++.h>
using namespace std;
int fibs(int n) {
if(n<=1) return 1;
else return fibs(n-1)+fibs(n-2);
}
int main()
{
int n;
cin>>n;
cout<<fibs(n)<<endl;
return 0;
}
第二种写法:
#include<bits/stdc++.h>
using namespace std;
int fibs(int n) {
int first=0;
int second=1;
for(int i=0;i<n-1;i++) {
int sum=first+second;
first=second;
second=sum;
}
return second;
}
当我们输入1,5,28,45等这些数时发现,两种方法均可瞬间得出我们想要的结果,但当我们输入的数为64时,会出现一个问题:方法一迟迟得不出结果,而方法二却是瞬间得出来结果。why?
如何评价一个算法的好坏?
如果单从执行效率上进行评估,可能会想到这么一种方案:比较不同算法对同一组输入的执行处理时间 (事后统计法)
上述方案有比较明显的缺点
- 执行时间严重依赖硬件以及运行时各种不确定的环境
- 必须编写相应的测算代码
- 测试数据的选择比较难保证公正性
一般从以下维度来评估算法的优劣
- 正确性、可读性、健壮性(对不合理输入的反应能力)
- 时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
- 空间复杂度(space complexity):估算所需占用的内存空间
时间复杂度的估算
//执行次数为1
int f(int n) {
if(n>10) cout<<"n>10"<<endl;
else if(n>5) cout<<"n>5"<<endl;
else cout<<"n<=5"<<endl;
}
//执行次数为1+4+4+4=13
for(int i=0;i<4;i++) {
cout<<"test"<<endl;
}
//执行次数为1+n+n+n=3n+1;
for(int i=0;i<n;i++) {
cout<<"test"<<endl;
}
//执行次数为1+n+n+n*(1+3n)=3n^2+3n+1
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) {
cout<<"test"<<endl;
}
}
//执行次数为1+n+n+n*(1+15*3)=48n+1
for(int i-0;i<n;i++) {
for(int j=0;j<15;j++) {
cout<<"test"<<endl;
}
}
//执行次数为log2(n)
while(n=n/2) {
cout<<"test"<<endl;
}
//执行次数为1+log2(n)+log2(n)+log2(n)*(1+3n)=1+3*log2(n)+2*nlog2(n)
for(int i=0;i<n;i+=i) {
for(int j=0;j<n;j++) {
cout<<"test"<<endl;
}
}
/*i+=i即为i=i*2*/
大O表示法(Big O)
- 一般用大O表示法来描述复杂度,它表示的数据规模n对应的复杂度
- 忽略常数、系数、低阶
- 9——O(1)
- 2n+3——O(n)
- n^2 +2n+6——O(n^2)
- 4n^2+ 3n^2+ 22n+100——O(n^3)
对数阶的细节
对数阶一般省略底数
log2(n)=log2(9)*log9(n)
所以log2(n)、log9(n)统称为logn
所以根据大O表示法上面的程序的算法复杂度可以表示为
O(1)、O(1)、O(n)、O(n^2)、O(n)、O(logn)、O(nlog2(n))
对斐波那契数复杂度的分析
#include<bits/stdc++.h>
using namespace std;
int fibs(int n) {
if(n<=1) return 1;
else return fibs(n-1)+fibs(n-2);
}
int main()
{
int n;
cin>>n;
cout<<fibs(n)<<endl;
return 0;
}
假如输入的是5,则可得如下图解
1+2+4+8=2^0 +2^1 +2^2 +2^3 =2^4 -1=2^(n-1)-1 =0.5*2^n -1
所以复杂度为O(2^n)
算法的优化方向
- 用尽量少的储存空间
- 用尽量少的执行步骤(执行时间)
- 根据情况,可以
- 空间换时间(字符串哈希)
- 时间换空间