• 斐波那契问题


    斐波那契数列,指的是这样一个数列: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 ),在海量数据的查找过程中,这种细微的差别可能会影响最终的查找效率。

  • 相关阅读:
    (floyd+DP) zoj 3027
    (树形DP) acdream 1028
    acdream 1032
    (BFS) acdream 1191
    (树形DP) bzoj 1060
    (状态压缩DP) poj 2978
    (DP) codeforces 358D
    (DP+二分) hdu 3433
    (最大生成树) hdu 3367
    hdoj 3501
  • 原文地址:https://www.cnblogs.com/teroy/p/2952979.html
Copyright © 2020-2023  润新知