对费波纳契数列的几种算法实现对比分析
随笔结构:
一、初识
二、代码实现
三、研究分析
一、初识
“如果一对兔子每月能生1对小兔子,而每对小兔在它出生后的第3个月裏,又能开始生1对小兔子,假定在不发生死亡的情况下,由1对初生的兔子开始,1年后能繁殖成多少对兔子?”
斐波拉契把推算得到的头几个数摆成一串:1,1,2,3,5,8……
这串数里隐含着一个规律:从第3个数起,后面的每个数都是它前面那两个数的和。而根据这个规律,只要作一些简单的加法,就能推算出以后各个月兔子的数目了。
于是,按照这个规律推算出来的数,构成了数学史上一个有名的数列。大家都叫它“斐波拉契数列”,又称“兔子数列”。
这个数列有许多奇特的的性质,例如,从第3个数起,每个数与它后面那个数的比值,都很接近于0.618,正好与大名鼎鼎的“黄金分割律”相吻合。人们还发现,连一些生物的生长规律,在某种假定下也可由这个数列来刻画呢。
斐氏本人对这个数列并没有再做进一步的探讨。直到十九世纪初才有人详加研究,1960年左右,许多数学家对斐波拉契数列和有关的现象非常感到兴趣,不但成立了斐氏学会,还创办了相关刊物,其后各种相关文章也像斐氏的兔子一样迅速地增加。
斐波拉契数列的来源及关系斐波拉契数列的来源及关系斐波拉契数列的来源及关系斐波拉契数列的来源及关系
斐波拉契(Fibonacci)数列来源于兔子问题,它有一个递推关系, f(1)=1 f(2)=1 f(n)=f(n-1)+f(n-2),其中n>=2 {f(n)}即为斐波拉契数列。
二、代码实现
在C语言中的实现
1 #include<stdio.h> 2 void main() 3 { 4 long int f1,f2; 5 f1=1; 6 f2=1; 7 for(i=1;i<=20;i++) 8 { 9 printf("%d\n",f1); 10 printf("%d\n",f2); 11 f1=f1+f2; 12 f2=f2+1; 13 } 14 }
在C#中的实现(递归)
namespace 测试 { class Program { static void Main(string[] args) { for (int i = 1; i <= 20; i++) { int j=Test(i); Console.WriteLine("第" + i + "代有" + j + "只"); } Console.ReadLine(); } public static int Test(int i) { if(i<=2) { return 1; } else { return Test(i - 1) + Test(i-2); } } } }
另外一种高效解法
namespace _03斐波拉契数列_生小兔子问题_ { class Program { static void Main(string[] args) { Fibonacci.GetNumber(Convert.ToInt32(Console.ReadLine())); Console.ReadLine(); } } static class Fibonacci { private static List<long> _Fibonacci; static Fibonacci() { _Fibonacci = new List<long>(); _Fibonacci.Add(1); _Fibonacci.Add(1); } private static void Update(int to) { while(_Fibonacci.Count<=to) { int count = _Fibonacci.Count; long Ftemp = _Fibonacci[count - 1] + _Fibonacci[count - 2]; _Fibonacci.Add(Ftemp); Console.WriteLine(Ftemp); } } public static long GetNumber(int index) { if (_Fibonacci.Count < (index + 1)) { Update(index); } return _Fibonacci[index]; } } }
运用动态规划的实现
重新定义费波纳契的数列:0,1,1,2,3,5,8,13,21,34,55......
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace _03运用动态规划实现费波纳契数列 { class Program { static void Main(string[] args) { Console.WriteLine("请输入你要查找的数字的序号(从1开始)"); int numIn; int.TryParse(Console.ReadLine(), out numIn); long numOutResult=interFib(numIn); Console.WriteLine("序号为{0}的数字是{1}",numIn,numOutResult); Console.ReadKey(); } /// <summary> /// 函数的具体实现 /// </summary> /// <param name="n">数组的长度</param>3 /// <returns></returns> static long interFib(int n) { int[]val=new int[n]; if (n <= 2) { return n - 1; } else { val[1] = 1; val[2] = 1; for (int i = 3; i <= n - 1; i++) { val[i] = val[i - 1] + val[i - 2]; } return val[n-1]; } } } }
对动态规划的改进
static long interFib03(int n) { long last, nextLast, result; last = 1; nextLast = 0; result = 1; if (n <=2) { return n-1; } else { for (int i = 2; i <= n - 1; i++) { result = nextLast + last; nextLast = last; last = result; } return result; } }
三、对比分析
将递归算法和动态规划设计的算法进行对比
static void Main(string[] args) { Console.WriteLine("我们采用的数列是0,1,1,2,3,5,8......\n请输入你要查找的数字的序号(从1开始)"); //接收用户输入的数字 int numIn; int.TryParse(Console.ReadLine(), out numIn); //动态规划 DateTime startTime01; startTime01 = DateTime.Now; TimeSpan endTime01; long numOutResult01 = interFib01(numIn); endTime01 = DateTime.Now.Subtract(startTime01); //递归 DateTime startTime02; startTime02 = DateTime.Now; TimeSpan endTime02; long numOutResult02 = interFib02(numIn); endTime02 = DateTime.Now.Subtract(startTime02); Console.WriteLine("运用动态规划实现:序号为{0}的数字是{1},耗时{2}", numIn, numOutResult01,endTime01.ToString()); Console.WriteLine("运用递归方法实现:序号为{0}的数字是{1},耗时{2}",numIn,numOutResult02,endTime02.ToString()); Console.ReadKey(); }
结果:
递归算法的问题在于递归过程中会重复计算太多的数值
使用动态规划技术设计的算法从解决最简单的可解子问题开始入手,利用解决方案解决更加复杂的子问题直到解决整个问题为止,多数采用数组。