zoukankan      html  css  js  c++  java
  • 波非那切数列

    斐波那契数列介绍及Python中五种方法斐波那契数列

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/chichu261/article/details/83589767

    Q:斐波那契数列为什么那么重要,所有关于数学的书几乎都会提到?
    A:因为斐波那契数列在数学和生活以及自然界中都非常有用。

    在这里插入图片描述

    1. 斐波那契数列 概念引入

    斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

    数学上,斐波那契数列以递归的形式进行定义:
    F0=0F1=1Fn=Fn1+Fn2F0=0F1=1Fn=Fn−1+Fn−2 F_{0} = 0\F_{1} = 1\F_{n} = F_{n-1} + F_{n-2} F0=0F1=1Fn=Fn1+Fn2

    场景

    先来开看看“兔子数列”以及其他数学应用场景!!

    1. 1 兔子数列

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

    在这里插入图片描述

    1.2 排列组合

    有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?

    更多数学方面知识,请戳:
    斐波那契数列

    斐波那契数列为什么那么重要,所有关于数学的书几乎都会提到?

    2. 数列数学方法解答

    2.1 兔子繁殖问题

    斐波那契数列又因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”。

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

    我们不妨拿新出生的一对小兔子分析一下:

    第一个月小兔子没有繁殖能力,所以还是一对

    两个月后,生下一对小兔对数共有两对

    三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对

    ------

    依次类推可以列出下表:

    经过月数123456789101112
    幼仔对数 1 0 1 1 2 3 5 8 13 21 34 55
    成兔对数 0 1 1 2 3 5 8 13 21 34 55 89  
    总体对数 1 1 2 3 5 8 13 21 34 55 89 144  

    幼仔对数=前月成兔对数

    成兔对数=前月成兔对数+前月幼仔对数

    总体对数=本月成兔对数+本月幼仔对数

    可以看出幼仔对数、成兔对数、总体对数都构成了一个数列。这个数列有关十分明显的特点,那是:

    前面相邻两项之和,构成了后一项。

    2.2 排列组合
    2.2.1 跨楼梯组合

    有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?

    这就是一个斐波那契数列:登上第一级台阶有一种登法;登上两级台阶,有两种登法;登上三级台阶,有三种登法;登上四级台阶,有五种登法……

    1,2,3,5,8,13……所以,登上十级,有89种走法。

    2.2.2 掷硬币不连续情形

    一枚均匀的硬币掷10次,问不连续出现正面的可能情形有多少种?

    答案是:
    1/5)[(1+5)/2](10+2)[(15)/2](10+2)=144(1/√5)∗[(1+√5)/2](10+2)−[(1−√5)/2](10+2)=144 (1/√5)*{[(1+√5)/2]^(10+2) - [(1-√5)/2]^(10+2)}=144 1/5)[(1+5)/2](10+2)[(15)/2](10+2)=144

    3. Python代码实现斐波那契数列

    时间复杂度

    空间复杂度

    通过时间复杂度空间复杂度评判代码的执行效率。

    • 例如:从规模上来说,如果需要计算F(4)的值,需要进行9次元素操作

      设T(n)为计算n的时间复杂度,那么
      T(n)=T(n1)+T(n2)+O(1)T(n)=T(n−1)+T(n−2)+O(1) T(n) = T(n-1) + T(n-2)+O(1) T(n)=T(n1)+T(n2)+O(1)
      一般情况,可以得出:T(n)&lt;2T(n1)+O(1)T(n)&lt;2∗T(n−1)+O(1) T(n)&lt; 2* T(n-1) + O(1) T(n)<2T(n1)+O(1)

      粗略估算,Tn&lt;O2nT(n)&lt;O(2n) T(n) &lt; O(2^n) Tn<O2n),上述代码求解F(n)的计算,它的时间复杂度是$O(2^n)

    3.1 python特有写法

    打印正整数n之内的斐波那契数列

    # Python特有, 常规写法
    def fib(self, n):
    	a = 0
    	b = 1
    	while a <= n:
    		print(a, end=" ", flush=True)
    		a, b = b, a + b  # python不借助变量交换两数的值
    
    fib(100)  # 求n之内的斐波那契数列
     

    O(n)O(1)时间复杂度:O(n),空间复杂度:O(1) 时间复杂度:O(n),空间复杂度:O(1) O(n)O(1)

    3.2 递归

    打印斐波那契数列前10位数字

    # 递归
    def fibonacci(i):
        num_list = [0, 1]
        if i < 2:
            return num_list[i]
        elif i >= 2:
            return (fibonacci(i - 2) + fibonacci(i - 1))
    
    
    print(fibonacci(10))
     

    O(n)O(n)时间复杂度:O(n),空间复杂度:O(n) 时间复杂度:O(n),空间复杂度:O(n) O(n)O(n)

    3.3 类对象
    # 迭代的方式
    class FibIterator(object):
        """斐波那契数列迭代器"""
        def __init__(self, n):
            """
            :param n: int, 指明生成数列的前n个数
            """
            self.n = n
            # current用来保存当前生成到数列中的第几个数了
            self.current = 0
            # num1用来保存前前一个数,初始值为数列中的第一个数0
            self.num1 = 0
            # num2用来保存前一个数,初始值为数列中的第二个数1
            self.num2 = 1
    
        def __next__(self):
            """被next()函数调用来获取下一个数"""
            if self.current < self.n:
                num = self.num1
                self.num1, self.num2 = self.num2, self.num1+self.num2
                self.current += 1
                return num
            else:
                raise StopIteration
    
        def __iter__(self):
            """迭代器的__iter__返回自身即可"""
            return self
    
    
    if __name__ == '__main__':
        fib = FibIterator(10)
        for num in fib:
            print(num, end=" ")
     
    3.4 矩阵解决问题

    从定义开始:
    F0=0F1=1Fn=Fn1+Fn2F0=0F1=1Fn=Fn−1+Fn−2 F_{0} = 0\F_{1} = 1\F_{n} = F_{n-1} + F_{n-2} F0=0F1=1Fn=Fn1+Fn2
    转化为矩阵形式
    (Fn+1Fn)=(1110)(FnFn1)(Fn+1Fn)=(1110)∗(FnFn−1) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}=egin{pmatrix}1&amp;1\ 1&amp;0 end{pmatrix}*egin{pmatrix}F_{n}\ F_{n-1} end{pmatrix} (Fn+1Fn)=(1110)(FnFn1)
    可以得出:(Fn+1Fn)(1110)(F1F0)(Fn+1Fn)(1110)(F1F0) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}egin{pmatrix}1&amp;1\ 1&amp;0 end{pmatrix}egin{pmatrix}F_{1}\ F_{0} end{pmatrix} (Fn+1Fn)(1110)(F1F0)
    我们设定
    A=(1110)A=(1110) A=egin{pmatrix}1&amp;1\ 1&amp;0 end{pmatrix} A=(1110)
    很显然可以变为如下:
    A=(F2F1F1F0)A=(F2F1F1F0) A=egin{pmatrix}F_{2}&amp;F_{1}\ F_{1}&amp;F_{0} end{pmatrix} A=(F2F1F1F0)
    通过数学归纳法可以推出以下公式:
    An=(Fn+1FnFnFn1)=(1110)nAn=(Fn+1FnFnFn−1)=(1110)n A^{n}=egin{pmatrix}F_{n+1}&amp;F_{n}\ F_{n}&amp;F_{n-1} end{pmatrix}=egin{pmatrix}1&amp;1\ 1&amp;0 end{pmatrix}^{n} An=(Fn+1FnFnFn1)=(1110)n
    很显然计算F(n)的值,只需要进行矩阵的n次幂运算,取出结果矩阵第二行第一个元素值即可
    O(n)O(1)时间复杂度:O(n),空间复杂度:O(1) 时间复杂度:O(n),空间复杂度:O(1) O(n)O(1)

    这里可以利用快速幂运算求解,假设计算A的N次幂,二阶矩阵的乘法满足结合律

    设A,B,C都是任意的二阶矩阵,则
    A(BC)=(AB)CA(BC)=(AB)C A(BC)=(AB)C A(BC)=(AB)C

    我们设定:m=[n2]m=[n2] m=[frac{n}{2}] m=[2n]

    • 当n为偶数: AN=AmAmAN=Am∗Am A^{N}=A^{m}∗A^{m} AN=AmAm

    • 当n为奇数: AN=AmAmAAN=Am∗Am∗A A^{N}=A^{m}∗A^{m}∗A AN=AmAmA

      相当于A6=A3A3A7=A3A3AA6=A3∗A3,A7=A3∗A3∗A A^{6}=A^3∗A^3,A^7=A^3∗A^3∗A A6=A3A3A7=A3A3A

    这样可以减少计算次数,因为A6=AAAAAAA6=A∗A∗A∗A∗A∗A A6=A∗A∗A∗A∗A∗A A6=AAAAAA这里有5个乘,A6=AAAAAAA6=(A∗A∗A)∗(A∗A∗A) A6=(A∗A∗A)∗(A∗A∗A) A6=AAAAAA) 计算完AAAA∗A∗A A*A*A AAA 得到结果A3A3 A^3 A3,再乘以A3A3 A^3 A3 这里用了3个乘

    以下是普通数据的快速幂运算,运算改为矩阵乘法,ret改为单位矩阵即可

    def qpow(base, exp):
        if exp == 0:
            return 1
        ret = 1
        while exp:
            if exp & 1:
                ret = ret * base
            base = base * base
            exp >>= 1
        return ret
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.5 矩阵再推导

    我们可以设定: n=2mn=2m n=2m n=2m
    那么
    A2m=(F2m+1F2mF2mF2m1)=AmAmA2m=(F2m+1F2mF2mF2m−1)=Am∗Am A^{2m}=egin{pmatrix}F_{2m+1}&amp;F_{2m}\ F_{2m}&amp;F_{2m-1} end{pmatrix}=A^{m}*A^{m} A2m=(F2m+1F2mF2mF2m1)=AmAm
    已知
    Am=(Fm+1FmFmFm1)Am=(Fm+1FmFmFm−1) A^{m}=egin{pmatrix}F_{m+1}&amp;F_{m}\ F_{m}&amp;F_{m-1} end{pmatrix} Am=(Fm+1FmFmFm1)
    所以:
    (Fm+1FmFmFm1)(Fm+1FmFmFm1)=(F2m+1F2mF2mF2m1)(Fm+1FmFmFm−1)∗(Fm+1FmFmFm−1)=(F2m+1F2mF2mF2m−1) egin{pmatrix}F_{m+1}&amp;F_{m}\ F_{m}&amp;F_{m-1} end{pmatrix}* egin{pmatrix}F_{m+1}&amp;F_{m}\ F_{m}&amp;F_{m-1} end{pmatrix}=egin{pmatrix}F_{2m+1}&amp;F_{2m}\ F_{2m}&amp;F_{2m-1} end{pmatrix} (Fm+1FmFmFm1)(Fm+1FmFmFm1)=(F2m+1F2mF2mF2m1)
    计算后可以得出:
    (F2m+1F2m)=(F2m+1+F2mFm(Fm+1+Fm1))(F2m+1F2m)=(Fm+12+Fm2Fm∗(Fm+1+Fm−1)) egin{pmatrix}F_{2m+1}\ F_{2m} end{pmatrix}=egin{pmatrix}F_{m+1}^{2}+F_{m}^{2}\ F_{m}*(F_{m+1}+F_{m-1}) end{pmatrix} (F2m+1F2m)=(Fm+12+Fm2Fm(Fm+1+Fm1))

    这里需要注意一点 n 需要进行奇偶性判定:

    • 当n为奇数时:m=[n2]n=2m+1m=[n2],n=2∗m+1 m=[frac{n}{2}],n=2*m+1 m=[2n]n=2m+1 此时,(Fn+1Fn)(F2m+2F2m+1)(Fm+1(Fm+2+Fm)F2m+1+F2m)(Fn+1Fn)(F2m+2F2m+1)(Fm+1∗(Fm+2+Fm)Fm+12+Fm2) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}egin{pmatrix}F_{2m+2}\ F_{2m+1} end{pmatrix}egin{pmatrix}F_{m+1}*(F_{m+2}+F_{m})\ F_{m+1}^{2}+F_{m}^{2}end{pmatrix} (Fn+1Fn)(F2m+2F2m+1)(Fm+1(Fm+2+Fm)Fm+12+Fm2)
      由于 Fm+2=Fm+1+FmFm+2=Fm+1+Fm F_{m+2}=F_{m+1}+F_{m} Fm+2=Fm+1+Fm ,因此,可以推导出
      (Fn+1Fn)=(Fm+1(Fm+1+2Fm)F2m+1+F2m)(Fn+1Fn)=(Fm+1∗(Fm+1+2∗Fm)Fm+12+Fm2) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}=egin{pmatrix}F_{m+1}*(F_{m+1}+2*F_{m})\ F_{m+1}^{2}+F_{m}^{2}end{pmatrix} (Fn+1Fn)=(Fm+1(Fm+1+2Fm)Fm+12+Fm2)
    • 当n为偶数时:m=n2n=2mm=n2,n=2∗m m=frac{n}{2},n=2*m m=2nn=2m,此时
      (Fn+1Fn)=(F2m+1F2m)=(F2m+1+F2mFm(Fm+1+Fm1))(Fn+1Fn)=(F2m+1F2m)=(Fm+12+Fm2Fm∗(Fm+1+Fm−1)) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}=egin{pmatrix}F_{2m+1}\ F_{2m} end{pmatrix}=egin{pmatrix}F_{m+1}^{2}+F_{m}^{2}\ F_{m}*(F_{m+1}+F_{m-1}) end{pmatrix} (Fn+1Fn)=(F2m+1F2m)=(Fm+12+Fm2Fm(Fm+1+Fm1))
      由于 Fm+2=Fm+1+FmFm+2=Fm+1+Fm F_{m+2}=F_{m+1}+F_{m} Fm+2=Fm+1+Fm,因此,可以推导出:
      (Fn+1Fn)=(F2m+1+F2mFm(2Fm+1Fm))(Fn+1Fn)=(Fm+12+Fm2Fm∗(2∗Fm+1−Fm)) egin{pmatrix}F_{n+1}\ F_{n} end{pmatrix}=egin{pmatrix}F_{m+1}^{2}+F_{m}^{2}\ F_{m}*(2*F_{m+1}-F_{m})end{pmatrix} (Fn+1Fn)=(Fm+12+Fm2Fm(2Fm+1Fm))

    所以计算F(N)的值,只需要知道F(n/2+1)和F(n/2)即可

    def fib(n):
        if n < 1:
            return (1, 0)
    
        f_m_1, f_m = fib(n >> 1)
        if n & 1:
            return f_m_1 * (f_m_1 + 2 * f_m), f_m ** 2 + f_m_1 ** 2
        else:
            return f_m_1 ** 2 + f_m ** 2, f_m * (2 * f_m_1 - f_m)
     

    O(log2n)O(1)时间复杂度:O(log⁡2n),空间复杂度:O(1) 时间复杂度:O(log_2 n),空间复杂度:O(1) O(log2n)O(1)

  • 相关阅读:
    nginx socket转发设置
    Linux CentOS 7 安装字体库 & 中文字体
    nginx配置location总结及rewrite规则写法
    nginx动静分离小示例
    iptables黑/白名单设置(使用ipset 工具)
    Docker logs 命令
    Docker定制容器镜像(利用Dockerfile文件)
    docker swarn集群笔记
    [国家集训队]数颜色 / 维护队列(带修莫队)
    于是他错误的点名开始了(trie树)
  • 原文地址:https://www.cnblogs.com/xingkongzhizhu/p/11996447.html
Copyright © 2011-2022 走看看