python编写递归函数、求斐波那契数列第n项_40.细说递归之二:Python求解斐波那契数列...

本篇通过青蛙跳台阶、兔子数列(斐波那契数列)问题进一步理解递归思想的魅力。

上一篇最后的题目如下:

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶总共有多少种跳法?

归纳推理方式

不妨假设跳上n级台阶的跳法总数是 f(n),我们无法直接得知f(n)和n的关系,也即无法直接给出函数f(n)的表达式。

当我们在求解一个未知问题时,如果这个问题的通用解无法直接给出,但是个别情况下的解是可以给出的话,习惯上,我们会观察个别情况下的解,看是否可以推理出通用解。这种思维方式叫归纳推理法。

归纳推理是我们认识世界的基础,也是我们最习惯的思维方式。

所以,我们从f(1)开始归纳推理

f(1) = 1 [1]

f(2) = 2 [11,2]

f(3) = 3 [111,12,21]

f(4) = 5 [1111,112,121,211,22]

f(5) = 8 [11111,1112,1121,1211,2111,122,212,221]

f(6) = 13

……

其中[ ] 内表示不同的跳法,比如f(3)有3种跳法,分别是

111:连跳3次1级台阶

12:先跳1次1级台阶,再跳1次2级台阶

21:先跳1次2级台阶,再跳1次1级台阶

尽管我们在列举的时候,可以使用一个小技巧。

比如列举 f(4) 的所有可能时

先列举不跳2级台阶的可能,只有1种:1111

再列举跳了1次2级台阶的可能,有3种:112,121,211

再列举跳了2次2级台阶的可能,有1种:22

但随着n的增大,列举起来还是挺麻烦的。

不过好在如果足够细心的话,从f(1)到f(6),已经可以开始归纳推理了,我们可以归纳推理出一个猜想:

f(n) = f(n-1) + f(n-2)

这个猜想对f(3),f(4),f(5),f(6)都是成立的

f(3) = f(2) + f(1) = 2+1 = 3

f(4) = f(3) + f(2) = 3+2 = 5

f(5) = f(4) + f(3) = 5+3 = 8

f(6) = f(5) + f(4) = 8+5 = 13

假设这个猜想是正确的,我们就可以用Python循环求任意f(n)了。

程序如下,稍微注意一下在循环里3个变量的使用,如果不是特别清楚的话,可以回顾一下《10.裴蜀定理和欧几里得算法快速求解倒水问题》,里面有介绍。

def frog(n):

a = 1

b = 2

temp = 0

for i in range(3,n+1):

temp = a + b

a = b

b = temp

return temp

print(frog(7))

递归方式

再来回顾一下递归的本质,它是一种思想,一种解决问题的思维方式:

将规模大的问题转化为规模小的相似子问题来解决。

那么对于问题 f(n),该青蛙跳上n级台阶总共有多少种跳法,是否也能能转化为规模更小的子问题来解决呢?

我们换个思路,假设组队玩王者,使用的决胜策略是等着英雄最后放一把必杀技。

假设有 x 种玩法等着关羽放必杀技获胜,有y种玩法等着诸葛亮放必杀技获胜,请问一共有多种获胜的玩法。很显然是 x+y

对于问题f(n)也一样,不管前面怎么跳。

最后一步,可以从 n-1级台阶跳1级台阶到 n,也可以从 n-2级台阶跳2级台阶到n。

因此可得,f(n) = f(n-1) + f(n-2)。

这就把规模大的问题,转化成了规模更小的相似问题了。问题是什么。

问题f(n) 是该青蛙跳上n级台阶总共有多少种跳法。

规模大的问题和小的问题之间的关系。

f(n) = f(n-1) + f(n-2)“递”的结束条件。

n == 1 时,f(n) = 1;n==2时, f(n) == 2

在“递”、“归”的过程中做什么。

在“递”的过程中,也就是从 n --> 1的过程,什么都不要做。

在“归”的过程中,也就是从 1-->n 的过程,进行计算。

也即程序中的 return f(n-1) + f(n-2)

def frog(n):

if n == 1 :

return 1

elif n == 2:

return 2

else:

return frog(n-1)+frog(n-2)

print(frog(7))

巩固练习:更著名的兔子序列

意大利中世纪数学家斐波那契以兔子繁殖为例子引入了一个数列,称为“兔子数列”。

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

可以先思考,再阅读下面的内容

可以先用归纳推理的方法:

f(1) = 1,[A],才出生的兔子A

f(2) = 1,[A],成熟的兔子A

f(3) = 2,[A,Aa],老兔子A和它的宝宝Aa

f(4) = 3,[A,Aa,Ab],老兔子A和成熟的宝宝Aa,才出生的宝宝Ab

f(5) = 5,[A,Aa,Ab,Ac,Aa1],老兔子A和它的三对宝宝Aa,Ab,Ac,其中最大的宝宝Aa也生了一对宝宝 Aa1

f(6) = 8 [A,Aa,Ab,Ac,Ad,Aa1,Aa2,Ab1] ……

……

我们发现,数列的规律和青蛙跳台阶是一样的,同样可以有如下猜测。

f(n) = f(n-1) + f(n-2)

如果用递归的思路,我们则可以更清晰地得到如上的结论。

以f(4)、f(5)、f(6)为例

f(4) = 3,[A,Aa,Ab],

f(5) = 5,[A,Aa,Ab,Ac,Aa1]

f(6) = 8 [A,Aa,Ab,Ac,Ad,Aa1,Aa2,Ab1] ……

f(6),也即第6个月中的兔子由2部分组成

蓝色的兔子:上个月存在的兔子,也即f(5)中的兔子,在这个月也一定存在。

红色的兔子:上上个月存在的兔子,也即f(4)就存在的兔子,在这个月一定会再生出一对新的兔子。A生了Ad,Aa生了Aa2,Ab生了Ab1。

所以,f(6) = f(5) + f(4)

对于所有的f(n),也都成立,也即f(n) = f(n-1) + f(n-2)

由于兔子数列最早是由意大利数学家斐波那契提出来的,所以也称为斐波那契数列。

有趣的是,这样一个完全是自然数的数列,通项公式却是用无理数来表达的。而且当n

趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618)。

越到后面,

的比值越接近黄金比,所以斐波那契又称为黄金分割数列。

更为神奇的是,自然界有非常多的现象都呈现出斐波那契数列的特征,比如比如松果、凤梨、树叶的排列、某些花朵的花瓣数(典型的有向日葵花瓣),蜂巢,蜻蜓翅膀等等,有兴趣的话可以百度搜索。

汉诺塔

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,有三根神柱。印度教的主神梵天在创造世界的时候,在其中一根柱子上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根柱子上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根柱子上移到另外一根柱子上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

问题是:假设有3个柱子:A,B,C和n片金片,如果要将全部金片按如上规则从A柱移动到C柱,请问该如何移动?

更多文章关注公众号,和孩子一起学Python

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值