fibonacci是一个非常有趣的数列,中文翻译是“斐波那契”,我则妄称之为"肥婆纳妾",而对于肥婆纳妾我也是才刚刚接触,为什么说它有趣呢,当然不会是因为肥婆居然会纳妾啦,鄙人理由有四,
一. 有趣的兔子问题。若大家还记得中学的兔子问题,便会恍然大悟,原来自己和肥婆纳妾早已是老相识了,有道是白头如新,倾盖如故,便有这几分含味。
斐波那契在《算盘书》中提出了一个有趣的兔子问题:
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔都不死,那么一年以后可以繁殖多少对兔子?
我们不妨拿新出生的一对小兔子分析一下:
第一个月小兔子没有繁殖能力,所以还是一对;
两个月后,生下一对小兔总数共有两对;
三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对;
……
依次类推可以列出下表:
二.有趣的通项公式。这是一个用无理数来表示有理数的公式,又叫比内公式,也就是说fibonacci的通项公式叫比内公式(即肥婆纳妾的条件是比内裤~~oh my god!),这个公式将是做这道题的关键之一,公式如下:
![](http://acm.hdu.edu.cn/forum/attachment/9_17558_66eb0d62d0d7087.jpg)
三.这肥婆居然和黄金分割有密切关系
有趣的是:这样一个完全是
自然数的数列,通项公式却是用
无理数来表达的。而且当n趋向于无穷大时,后一项与前一项的比值越来越逼近
黄金分割0.618.(或者说后一项与前一项的比值小数部分越来越逼近
黄金分割0.618、前一项与后一项的比值越来越逼近
黄金分割0.618)
1÷1=1,2÷1=2,3÷2=1.5,5÷3=1.666...,8÷5=1.6,…………,89÷55=1.6181818…,…………233÷144=1.618055…75025÷46368=1.6180339889…...
越到后面,这些比值越接近黄金比.
四.自然界的fibonacci
斐波那契数列在自然科学的其他分支,有许多应用。例如,树木的生长,由于新生的枝条,往往需要一段“休息”时间,供自身生长,而后才能萌发新枝。所以,一株树苗在一段间隔,例如一年,以后长出一条新枝;第二年新枝“休息”,老枝依旧萌发;此后,老枝与“休息”过一年的枝同时萌发,当年生的新枝则次年“休息”。这样,一株树木各个年份的枝桠数,便构成斐波那契数列。这个规律,就是生物学上著名的“鲁德维格定律”。
回归题目,看到a[n] = a[n - 1] + a[n - 2],大伙肯定想着用递归,可是递归对于0 <= n <= 100000000这么大的数就显得有心无力了,从效率来说,一般的递归要进行2^100000000次调用,不算时间上的耽搁,就连栈都会溢出,所以并不可用。记忆递归的话,只需进行100000000次调用即可,效率貌似解决得了,可是fibonacci增长速度极快,在n达到100之前就溢出了(_int64比int稍后而已),而题目只要求算前四位,这又让人想起了强大的log10(n)来,不得不说,假设一个数m,m = 10^lg(m),众所周知,一个数用科学计数法表示,可以写成a+10^b而10^b只是改变a的位权,对其数值并不影响,因此我们就等于把123456789这样的数转换成1.23456789来表示(就不用担心溢出了,但不能拿来四则运算,浮点数计算有误差!!),接下来,只要用好fibonacci的公式就好了,我们来化解一下公式(我看网上的化解法)对公式取log10,取完
后如下
![](http://acm.hdu.edu.cn/forum/attachment/9_17558_9a6217715ff7acd.jpg)
此时我们分析题目,注意到两点
1.位数不大于四位的全部表示出来,而大于四位的则取其前四位。
2.当n越大时,取完log后的f(n)的最后一项越来越接近0,可以忽略不计,此时再用floor函数得到lg(f(n))的小数即可(10^1 = 10,小数的话就小于10,便是科学计数法的表示)
也就是说,在n < 21时,用记忆的递归就好了,在21以后就用公式,此时后项可忽略(n = 21时,答案才超过10000)
以下是个人代码:
#include<stdio.h>
#include<cmath>
int shu[10000];
int fibonacci(int n)
{
if(shu[n] > 0) return shu[n];
if(n == 0 || n == 1) return n;
else return shu[n] = fibonacci(n - 1) + fibonacci(n - 2);
}
int main()
{
int n;
shu[0] = 0;
shu[1] = 1;
while(scanf("%d",&n) == 1)
{
if(n < 21)
printf("%d\n",fibonacci(n));
else
{
double m;
m =(-0.5)*log10(5.0) + n * log10(((1.0 + sqrt(5.0))/2.0));
m = m - floor(m);
m = pow(10.0,m);
while(m < 1000) m *= 10;
printf("%d\n",(int)m);
}
}
return 0;
}