斐波那契问题

斐波那契数列,指的是这样一个数列:1、1、2、3、5、8、13、21...,除第1,2位的数为1外,其他数为前两位数字的相加之和。

1.斐波那契数列与经典兔子繁殖问题

一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔都不死,那么在有1个月大的一对兔子的条件下一年以后可以繁殖多少对兔子?

我们可以分析一下。

第一个月一对兔子A生出一对小兔B。

第二个月还是原来的一对兔子A生出一对小兔C。

第三个月A生出一对小兔D,B生出一对小兔E。

第四个月A生出一对小兔F,B生出一对小兔G,C生出一对小兔H。

依次类推......如下表所示:

月份 1 2 3 4 5 6 7 8 9 10 11 12
每个月新出生的兔子对数 1 1 2 3 5 8 13 21 34 55 89 144

 

 

一年后繁殖的兔子对数量为:1+1+2+3+5+8+13+21+34+55+89+144

2.C语言使用递归来实现斐波纳契数列

fibonacci.h
long fibonacci(int n);
递归实现菲波那契数列
#include <stdio.h>
#include <stdlib.h>
#include "fibonacci.h"


int main(void)
{
    int n;
    long result;
    scanf("%d",&n);
    result=fibonacci(n);
    printf("%ld",result);
    return EXIT_SUCCESS;
}

long fibonacci(int n)
{
    if( n>0 && n<=2 )
        return 1;
    else
        return fibonacci(n-1)+fibonacci(n-2);
}

递归带来的好处是使算法变得更加简明,可读性高。然而,使用递归也带来了额外的内存开销.可以考虑使用迭代实现斐波那契。

3.C语言使用迭代来实现斐波那契数列

迭代实现斐波那契数列
#include <stdio.h>
#include <stdlib.h>
#include "fibonacci.h"


int main(void)
{
    int n;
    long result;
    scanf("%d",&n);
    result=fibonacci(n);
    printf("%ld",result);
    return EXIT_SUCCESS;
}


long fibonacci(int n)
{
    long result,previous_result,temp;
    result = previous_result =1;
    while(n>2)
    {
        temp = result;
        result += previous_result;
        previous_result = temp;
        n -= 1;
    }
    return result;
}

原先自己的第一个思路是使用一个long数组存放斐波那契数列,在最后的第n位再把前两项的数字相加返。但这样做的话会浪费掉很多用不到的数组内存,所以改进为使用3个变量,一个记录前一个结果,一个记录当前结果,一个做为中间的temp变量,作为一个交换的介体。

4.斐波那契查找

斐波那契数列又称为黄金分割数列。为什么叫做黄金分割数列呢?理由是当n趋近于无穷大的时候,后一项和前一项的比值越来越接近黄金分割1.618.  3/2=1.5,5/3=1.666,8/5=1.6,13/8=1.625...

数学背景:数字0.618…更为数学家所关注,它的出现,不仅解决了许多数学难题(如:十等分、五等分圆周;求18度、36度角的正弦、余弦值等),而且还使优选法成为可能。优选法是一种求最优化问题的方法。如在炼钢时需要加入某种化学元素来增加钢材的强度,假设已知在每吨钢中需加某化学元素的量在1000—2000克之间,为了求得最恰当的加入量,需要在1000克与2000克这个区间中进行试验。通常是取区间的中点(即1500克)作试验。然后将试验结果分别与1000克和2000克时的实验结果作比较,从中选取强度较高的两点作为新的区间,再取新区间的中点做试验,再比较端点,依次下去,直到取得最理想的结果。这种实验法称为对分法。但这种方法并不是最快的实验方法,如果将实验点取在区间的0.618处,那么实验的次数将大大减少。这种取区间的0.618处作为试验点的方法就是一维的优选法,也称0.618法。实践证明,对于一个因素的问题,用“0.618法”做16次试验就可以完成“对分法”做2500次试验所达到的效果。因此大画家达·芬奇把0.618…称为黄金数。

斐波那契查找
#include <stdio.h>
#define MAX_SIZE 30
int fibonacci(int n);

int main (void)
{
    /*准备工作*/
    int n,i,key;
    printf("Please input n:");
    scanf("%d",&n);
    int list[MAX_SIZE];
    printf("Please input your sorted array:");
    for(i=1 ; i<=n ;i++)
    {
        scanf("%d",&list[i]);    
    }
    printf("Please input the key:");
    scanf("%d",&key);
    
    /*比较找到不小于n的斐波那契数的位置*/
    int k = 1;
    while(n>fibonacci(k))
    {
        k++;    
    }
    
    
    /*在n的个数不足找到的斐波那契数,用list【n】补齐*/
    for(i=n+1; i<=fibonacci(k); i++)
    {
        list[i]=list[n];
    }
    
    /*定义key的位置*/
    int pos = -1;
    
    int low,high,mid;
    low = 1;
    high = n;
    while(low <= high)
    {
        mid =low + fibonacci(k-1) -1;
        if(key < list[mid])
        {
            high=mid - 1;
            k = k-1;
        }
        else if(key > list[mid])
        {
            low=mid + 1;
            k = k-2;
        }
        else
        {
            if(mid<n)
            {
                pos = mid;
                break;
            }
            else
            {
                pos = n;
                break;
            }
        }
    }
    
    printf("The result position is %d.",pos);
    return 0;
}

int fibonacci(int n)
{
    int result,previous_result,temp;
    result = previous_result = 1;
    while(n>2)
    {
        temp=result;
        result+=previous_result;
        previous_result=result;
        n--;
    }
    return result;
}

 对斐波那契查找可能存在两个比较难理解的地方。

(1)

/*在n的个数不足找到的斐波那契数,用list【n】补齐*/
for(i=n+1; i<=fibonacci(k); i++)
{
    list[i]=list[n];
}

由于斐波那契查找是基于斐波那契数列的查找,要求有序表的个数必须是存在于斐波那契数列中的数,所以在数不足的情况下,如n=10,fibonacci(7)=13 > 10,必须将有序表的个数扩增到等于斐波那契数,则list[11]=list[12]=list[13]=list[n]。

(2)

if(key < list[mid])
{
    high=mid - 1;
    k = k-1;
}
else if(key > list[mid])
{
    low=mid + 1;
    k = k-2;
}

在key<list[mid] 的情况下 k=k-1,ket > list [mid] 的情况下 k=k-2。这又是为什么呢?

对于斐波那契查找,分割是从mid =low + fibonacci(k-1) -1 开始的,现在数组的长度为fibonacci(k),mid 将数组分为两个部分,前一部分为[1,mid],长度为fiboncaai(k-1),则后一部分的长度为fibonacci(k)-fibonacci(k-1),根据斐波那契的性质,f(k) - f(k-1) = f(k-2),则后一部分的长度为fibonacci(k-2),所以当key > list[mid] 的时候 k = k - 2。

斐波那契的时间复杂度也为O(log n),相对与择半查找进行加法与除法运算(mid=(low + high)/2 ),斐波那契查找只是最简单加减法运算( mid = low + fibonacci(k-1) -1 ),在海量数据的查找过程中,这种细微的差别可能会影响最终的查找效率。

posted @ 2013-03-26 21:21  teroy  阅读(1972)  评论(5编辑  收藏  举报