分治算法-计算Fibonacci数列
- 实验目的
通过朴素递归算法、自底向上算法和分治算法的比较理解如何利用分支思想设计一个高效算法。
- 实验内容
- 编写函数实现用朴素递归算法计算Fibonacci数列;
- 编写函数实现用自底向上算法计算Fibonacci数列;
- 编写函数实现用分治算法计算Fibonacci数列;
- 测试n取10、40、45、50的情况下三种算法各自的运行时间,通过对比分析研究哪个算法在数据量逐渐增大时效率高;
- 对三种算法进行理论分析哪个算法效率高;
- 与实验数据对照,检查是否实验结果验证了理论分析的结果。
- 当n取50时运行结果是怎样的,分析结果异常的原因。
- 算法伪代码
朴素递归算法伪代码:
Simple(n)
if n =0
return 0
if n == 1 || n == 2
return 1
return Simple(n -1)+Simple(n - 2)
自底向上算法伪代码:
Down_To_Up(n)
if n =0
return 0
else if n == 1 || n == 2
return 1
else
f[0] = 0
f[1] = 1
f[2] = 1
for i = 3 to n
f[i] = f[i - 1] + f[i - 2]
return f[n]
分治算法伪代码:
Divide_Conquer( A, n)
{
w= {1,0,0,1}
q=w
if n = 0
return q
else if n =1
return A
else if n%2=0
return one(Divide_Conquer(A, n / 2), Divide_Conquer(A, n / 2))
else
return one(one(Divide_Conquer(A, (n - 1) / 2), Divide_Conquer(A, (n - 1) / 2)), A)
- 理论分析
朴素递归算法为Θ(φ^n)时间复杂度,自底向上算法为Θ(n)时间复杂度,分治算法为Θ(lgn)时间复杂度。自底向上算法的时间复杂度分治算法的时间复杂度<朴素算法的时间复杂度。由于斐波那契序列随着项数增加,使用递归调用时前四十项求解没有问题,但五十项及以后的时候会出现内存溢出,输出为错误结果。所以要想求出更多的项必须使用非递归的方法求解。
- 测试用例
- 性能测试
-
测试结果示例:(单位:微秒)
算法名称
运行时间
数据量
10
40
45
50
平均运行时间
朴素递归算法
1
904722
1.02134e+007
1.4857e+008
Θ(φ^n)
自底向上算法
0.3
0.3
0.4
0.9
Θ(n)
分治算法
1.7
4.9
5.2
13.1
Θ(lgn)
源代码
-
#include <iostream> #include<algorithm> #include<cstdio> #include<set> #include<windows.h> const int INF = 1e8; typedef long long ll; using namespace std; struct node { ll m[2][2]; }; node A = { 1, 1, 1, 0 }; node one(node a,node b) { node c; ll i, j, k; for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { c.m[i][j] = 0; for (k = 0; k <= 1; k++) { c.m[i][j] += a.m[i][k] * b.m[k][j]; } } } return c; } /*朴素算法*/ int Simple(ll n) { if(n == 0) { return 0; } if(n == 1 || n == 2) { return 1; } return Simple(n -1)+Simple(n - 2); } /*分治算法*/ node Divide_Conquer(node A, ll n) { node q,w; w= {1,0,0,1}; q=w; if (n == 0) return q; else if (n == 1) return A; else if (n%2==0) return one(Divide_Conquer(A, n / 2), Divide_Conquer(A, n / 2)); else return one(one(Divide_Conquer(A, (n - 1) / 2), Divide_Conquer(A, (n - 1) / 2)), A); } /*自底向上算法*/ int Down_To_Up(ll n) { if(n == 0) { return 0; } else if(n == 1 || n == 2) { return 1; } else { ll f[n + 1]; //f f[0] = 0; f[1] = 1; f[2] = 1; for(ll i = 3; i <= n; i++) { f[i] = f[i - 1] + f[i - 2]; } return f[n]; } } int main() { ios::sync_with_stdio(false);//C++为了兼容C而采取的保守措施 cin.tie(0), cout.tie(0);//减少cin,cout时间 ll n; while(~scanf("%lld",&n)) { double run_time; _LARGE_INTEGER time_start; //开始时间 _LARGE_INTEGER time_over; //结束时间 double dqFreq; //计时器频率 LARGE_INTEGER f; //计时器频率 QueryPerformanceFrequency(&f); dqFreq = (double)f.QuadPart; /*朴素算法*/ QueryPerformanceCounter(&time_start); //计时开始 ll ans1 =Simple(n); QueryPerformanceCounter(&time_over); //计时结束 run_time = 1000000 * (time_over.QuadPart - time_start.QuadPart) / dqFreq;//乘以1000000把单位由秒化为微秒,精度为1000 000/(cpu主频)微秒 cout << "朴素算法时间: " << run_time << endl; cout << "朴素算法结果: " << ans1 << endl; /*自底向上算法*/ QueryPerformanceCounter(&time_start); //计时开始 ll ans2 =Down_To_Up(n); QueryPerformanceCounter(&time_over); //计时结束 run_time = 1000000 * (time_over.QuadPart - time_start.QuadPart) / dqFreq;//乘以1000000把单位由秒化为微秒,精度为1000 000/(cpu主频)微秒 cout << "自底向上时间: " << run_time << endl; cout << "自底向上结果: " << ans2 << endl; /*分治算法*/ QueryPerformanceCounter(&time_start); //计时开始 node a= Divide_Conquer(A, n - 1); QueryPerformanceCounter(&time_over); //计时结束 run_time = 1000000 * (time_over.QuadPart - time_start.QuadPart) / dqFreq;//乘以1000000把单位由秒化为微秒,精度为1000 000/(cpu主频)微秒 cout << "分治算法时间: " << run_time << endl; cout << "分治算法结果: " << a.m[0][0] << endl; } return 0; }