斐波那契数列及Python实现

斐波那契数列,又称为黄金分割数列,因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21……在数学上,斐波那契数列以如下被以递归的方法定义:F(0)=1,F(1)=1,F(n)=F(n-1)+F(n-2)(n>2,n∈N*),在现代物理、准晶体结构、化学等领域都有直接的引用。

兔子繁殖问题:

举例,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?
我们不妨拿新出生的一对小兔子分析一下:
第一个月小兔子没有繁殖能力,所以还是一对
两个月后,生下一对小兔对数共有两对
三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对。

依次类推可以列出下表:

进过月数 0 1 2 3 4 5 6 7 8 9 10 11 12
幼崽对数 1 0 1 1 2 3 5 8 13 21 34 55 89
成兔对数 0 1 1 2 3 5 8 13 21 34 55 89 144
总体对数 1 1 2 3 5 8 13 21 34 55 89 144 233

幼仔对数=前月成兔对数
成兔对数=前月成兔对数+前月幼仔对数
总体对数=本月成兔对数+本月幼仔对数
可以看出幼仔对数、成兔对数、总体对数都构成了一个数列。这个数列有关十分明显的特点,那是:前面相邻两项之和,构成了后一项。

台阶组合问题:

有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?
这就是一个斐波那契数列:登上第一级台阶有一种登法;登上两级台阶,有两种登法;登上三级台阶,有三种登法;登上四级台阶,有五种登法……
1,2,3,5,8,13……所以,登上十级,有89种走法。

类似的,一枚均匀的硬币掷10次,问不连续出现正面的可能情形有多少种?
答案是(1/√5)*{[(1+√5)/2]^(10+2) - [(1-√5)/2]^(10+2)}=144种。
求递推数列a⑴=1,a(n+1)=1+1/a(n)的通项公式
由数学归纳法可以得到:a(n)=F(n+1)/F(n),将斐波那契数列的通项式代入,化简就得结果。

动态规划中有一个例子是台阶算法的,其实也是斐波那契数列的一个实例请参考:漫画:什么是动态规划?https://juejin.im/post/5a29d52cf265da43333e4da7

附上一个Python的实现:

# -*- coding: utf-8 -*-
def fib(n):
    global numCalls
    numCalls += 1
    print 'fib called with',n
    if n==0 or n==1:
        return 1
    else:
        return fib(n-1) + fib(n-2)   #递归调用
numCalls=0
n=6
res=fib(n)
print 'fib of',n,'=',res,'numCalls = ',numCalls


#memoization resolve overlapping sub problems
#默记法解决重叠子问题
def fastFib(n,memo):
    global numCalls
    numCalls += 1
    print 'fib called with',n
    if not n in memo:
        memo[n]=fastFib(n-1,memo)+fastFib(n-2,memo)
    return memo[n]
def fib1(n):
    memo={0:1, 1:1}
    return fastFib(n,memo)
numCalls=0
n=6
res=fib1(n)
print 'fib of',n,'=',res,'numCalls = ',numCalls

运行结果:

fib called with 6
fib called with 5
fib called with 4
fib called with 3
fib called with 2
fib called with 1
fib called with 0
fib called with 1
fib called with 2
fib called with 1
fib called with 0
fib called with 3
fib called with 2
fib called with 1
fib called with 0
fib called with 1
fib called with 4
fib called with 3
fib called with 2
fib called with 1
fib called with 0
fib called with 1
fib called with 2
fib called with 1
fib called with 0
fib of 6 = 13 numCalls =  25

fib called with 6
fib called with 5
fib called with 4
fib called with 3
fib called with 2
fib called with 1
fib called with 0
fib called with 1
fib called with 2
fib called with 3
fib called with 4
fib of 6 = 13 numCalls =  11

可以看出我们使用默记法之后,省略了很多次递归调用的重新计算。

猜你喜欢

转载自blog.csdn.net/u011305680/article/details/80246320