查尔斯·巴贝奇是一名19世纪的英国发明家,也被说成是职业数学家。他曾经发明了差分机——一台能够按照设计者的意图,自动处理不同函数的计算过程的机器。这是一台硕大的、泛着微光的金属机器,包括数以千计加工精密的曲柄和齿轮。他在孤军奋战下造出的这台机器,运算精度达到了6位小数,能够算出好几种函数表。此后的实际运用证明,这种机器非常适合于编制航海和天文方面的数学用表。

  巴贝奇花费了漫长的一生来改进差分机,先是一种设想,然后是另一种设想。他曾设想用储存数据穿孔卡上的指令进行任何数学运算的可能性,并设想了现代计算机所具有的大多数其他特性。然而这些都超越了他所处的时代——巴贝奇的设想直到电子时代才得以完成——改进版的差分机最终没有变成现实。

  在一次科技展览会上,年轻而又光彩照人的奥古斯塔·爱达·拜伦看到了被称为“思考机器”的差分机试验品。那一刻,爱达确定了她一生的目标——完成这台精美的机器。接下来的几年里,爱达迅速学会了各种数学知识并拜入巴贝奇门下,终其一生让处理数的差分机变成处理信息的分析机。

  为了展示机器的威力,爱达曾设计了一个假想的程序,它循环运行,一次迭代的结果将成为下一次迭代的输入,每个函数前后相继,遵循相同的规则,巴贝奇将这个思路称为“机器咬尾巴——团团转”。然而这个程序仅仅存在于爱达的头脑中,直到她去世也没有生产出可以运行这个程序的机器。

  一个世纪后,爱达的梦想终于变成了现实,她的假想也成为程序员们的一种重要算法——递归。

递归关系式

  先来看一个表达式:

  表达式指出从第3项开始,每一项都是由前两项通过计算得出的,这类表达式就是递归关系式,有时也称为差分方程。为了能从递归关系式计算出序列中的每一项,必须知道序列开始的若干个数,这些数被称为初始条件或初始值。

  由于采取逐步计算的方式可以得到序列各项的值,所以很多时候得到递归关系是本身就是朝解决一个计数问题迈了一大步。有些时候甚至只能依赖递归关系进行计算。

不断繁殖的兔子——递归关系模型

  最著名的递归模型当属斐波那契(Fibonacci)数列,它最早出现在1202年。

  斐波那契提出了一个关于兔子的问题:某一年的年底将一雄一雌两只兔子放进围场中。从第二年一月份开始,这对兔子生下了一双儿女。此后每一对兔子在第二个月都能产下一对龙凤胎。一年后,围场里能有多少对兔子?

  这里的假定条件是不考虑兔子的近亲繁殖问题,也不考虑人类吃货造成的影响,并且每对兔子都能健康成长……简单而言就是不考虑科普,只关注数学模型。

递归表达

  在上一年12月放入一对兔子,次年一月,初始兔子将产下一对新兔子,所以一月份将有两对兔子。二月份,新兔子还没有长大,只有初始兔子能够生产一对兔子,因此二月底将有3对兔子。三月份,初始兔子和1月份诞生的新兔子都将生产一对兔子,再加上二月底本来就有的3对兔子,因此三月底将有2+3=5对兔子:

  如果令f(n)表示第n-1月末围场中的兔子总对数,那么可以总结出:

  可以利用这个关系计算出一年后的兔子总对数,即f(13),这需要知道f(1)到f(12)的值:

  一年后围场中有377对兔子。

  程序通常是从0开始的,因此我们可以令f(0) = 1,这就使得f(2)也满足f(n)的表达式,f(2)=f(1)+f(0),f(0),f(1),f(2),……,f(n)满足递归关系:

  这个序列就是著名的斐波那契序列,f(0)和f(1)是序列的初始值。

  知道了关系模型后很容易写出一段“机器咬尾巴”的代码:

 1 # 用递归计算斐波那契序列
 2 def fabo(n):
 3     if n < 2:
 4         return 1
 5     else:
 6         return fabo(n - 1) + fabo(n - 2)
 7
 8 if __name__ == '__main__':
 9     for i in range(2, 14):
10         print('f({0}) = {1}'.format(i, fabo(i)))

  打印结果:

避免无用功

  斐波那契的递归代码很优美,它能够让人以顺序的方式思考,然而这段代码只能作为递归程序的演示样品,并不能应用于实践。在运行时便会发现,当输入大于30时速度会明显变慢,普通家用机甚至无法计算出f(50)。变慢的关键在于每次计算f(n)都要重新求解f(n)前面的所有斐波那契数,相当于做了大量的无用功。

  知道症结后便可以对症下药,只要把所有计算过的数存储起来就能有效改进算法:

 1 # 存储所有计算过的斐波那契数
 2 fabo_list = [1, 1]
 3 # 用递归计算斐波那契序列
 4 def fabo_2(n):
 5     if n < len(fabo_list):
 6         return fabo_list[n]
 7     else:
 8         fabo_n = fabo_2(n - 1) + fabo_2(n - 2)
 9         fabo_list.append(fabo_n)
10
11         return fabo_n
12
13 if __name__ == '__main__':
14     for i in range(40, 51):
15         print('f({0}) = {1}'.format(i, fabo_2(i)))

  全局变量fabo_list将缓存所有计算过的值,这次可以快速计算f(40)~f(50)的值:

用循环代替递归

  所有的递归都可以用循环代替,因此递归的实现往往也有对应的循环版本,斐波那契序列也是如此:

 1 # 用循环计算斐波那契序列
 2 def fabo_3(n):
 3     fabo_list = [1] * (n + 1)
 4     for i in range(2, n + 1):
 5         fabo_list[i] = fabo_list[i - 1] + fabo_list[i - 2]
 6
 7     return fabo_list[n]
 8
 9 if __name__ == '__main__':
10     for i in range(1, 50):
11         print('f({0}) = {1}'.format(i, fabo_3(i)))

  这段代码的运行速度相当快,不仅预存储了所有计算过的斐波那契数,还比fabo_2省去了因递归导致的方法调用的时间。

  所有的递归都可以用循环代替,反过来也一样,所有循环也可以用递归代替,像Erlang这种编程语言,甚至没有定义while、for这种用于循环的语法,全部使用递归。

斐波那契序列的和

  斐波那契序列有很多有趣的性质,其中一个就是求和。

  用S(n)表示前n项的和:

  先来看一下S(3),利用f(1) = 1,S(3)可以转换为:

  由此可以大胆地推断:

  可以用数学归纳法证明这个结论:

  当n=1时,该结论S(1)=f(3)-1=3-1=2,f(0)+f(1)=2,此时结论正确;

  假设结论对于n=t-1成立,t是任意自然数:

  则当n=t时:

  由此可见结论是对的,它可以回答图中一共绘制了多少对兔子。

  求和的代码相当简单,不需要傻乎乎的去累加:

1 # 斐波那契序列的前n项之和
2 def fabo_sum(n):
3     return fabo_3(n + 2) - 1

  下一篇:递归关系的基本解法


   作者:我是8位的

  出处:http://www.cnblogs.com/bigmonkey

  本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

  扫描二维码关注公众号“我是8位的”

转载于:https://www.cnblogs.com/bigmonkey/p/10290162.html

递归的逻辑(1)——递归关系模型相关推荐

  1. varclus变量聚类对变量有啥要求_互助问答第208期:递归的双变量probit模型问题...

    递归的双变量probit模型的stata命令是什么?比如二元被解释变量为y1,内生的二元变量为t1,x1和x2是其他外生协变量,iv1为内生二元解释变量的工具变量,那么,递归双变量probit模型是否 ...

  2. python 线性回归模型_如何在Python中建立和训练线性和逻辑回归ML模型

    python 线性回归模型 Linear regression and logistic regression are two of the most popular machine learning ...

  3. 2020.08.20_Task1_基于逻辑斯特模型,实现分类预测

    Hello大家好,今天就来学习和了解一下逻辑斯特模型,以及它在机器学习领域中的一个重要用途:实现分类预测. 其实一开始看到标题是:"基于逻辑回归的分类预测",我挺疑惑的.一是完全不 ...

  4. 增长预测模型之逻辑斯蒂模型

    调用了macOS的系统部分来实现语音播报,这只是整个大看板中的初始部分,却是很能帮助理解指数增长和指数现象的规律以及sigmoid函数实际意义 from datetime import datetim ...

  5. 小红书流量逻辑、KOL模型、内容营销|2022千瓜历年研究汇总

    小红书平台专项课[品牌营销训练营]融合了千瓜历年研究和整理的成果,结合实际案例给到大家最全最干货的内容.本文选取了课程中的精华部分,为大家提供一份历年品牌营销投放的实操总结,助力2022年品牌营销增长 ...

  6. Python集成机器学习:用AdaBoost、决策树、逻辑回归集成模型分类和回归和网格搜索超参数优化

    最近我们被客户要求撰写关于集成机器的研究报告,包括一些图形和统计输出. Boosting 是一类集成机器学习算法,涉及结合许多弱学习器的预测. 视频:从决策树到随机森林:R语言信用卡违约分析信贷数据实 ...

  7. pyspark学习之——逻辑回归、模型选择与调参

    记录pyspark的MLlib库学习篇,学习资料来自spark官方文档,主要记录pyspark相关内容,要么直接翻译过来,要么加上自己的理解.spark2.4.8官方文档如下:https://spar ...

  8. 逻辑回归 + GBDT模型融合实战!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:吴忠强,东北大学,Datawhale成员 一.GBDT+LR简介 ...

  9. 【机器学习基础】逻辑回归 + GBDT模型融合实战!

    作者:吴忠强,东北大学,Datawhale成员 一.GBDT+LR简介 协同过滤和矩阵分解存在的劣势就是仅利用了用户与物品相互行为信息进行推荐, 忽视了用户自身特征, 物品自身特征以及上下文信息等,导 ...

最新文章

  1. 利用RANSAC算法筛选SIFT特征匹配
  2. TCP协议三次握手连接四次握手断开和DOS攻击
  3. 茅台防伪溯源服务器临时维护,如何使用茅台防伪溯源系统?能辨别茅台酒真假?...
  4. 计算机网络 数据段、报文、IP数据报、数据包、MAC帧的区别;应用层、运输层、网络层、数据链路层、物理层的区别与功能;转发器、集线器、网桥、交换机、路由器、网关的功能与区别
  5. 【Python基础】07、Python类与面向对象
  6. [Leedcode][JAVA][第4题][寻找两个正序数组中的中位数][二分查找][双指针]
  7. mysql 查外键关联关系 (指定被引用表,查哪些表对其有外键引用)
  8. 年底要算绩效了,高校青椒有多难?
  9. 滚动时间选择器recyclerview_Android自定义可循环的滚动选择器CycleWheelView
  10. javaFX2.0场景(Scene)分析及使用,多场景切换实现
  11. 安卓ROOT全教程(测试机 红米Note7Pro)
  12. 教你如何用Three.js创造一个三维太阳系
  13. 王师傅c语言游戏,炉石王师傅斗鱼首播!旭旭宝宝都送了10发超火,只有他没来...
  14. 家用小型监控器安装位置与功能
  15. TwinCAT3 C++ ——数字签名证书无法成功
  16. vue设置浏览器自动打开网址为 http://0.0.0.0:8080/ 的网页可能暂时无法连接,或者它已永久性地移动到了新网址。
  17. 关于学习java函数式接口Function中的apply方法的一些感悟
  18. Android7.0的适配
  19. C++输出流格式化方法
  20. Chrome浏览器批量离线下载迅雷快传资源

热门文章

  1. 联想电脑不显示蓝牙开关该如何操作
  2. 解决Flutter报错The method ‘File.create‘ has fewer named arguments than those of overridden method
  3. InSAR学习(六)形变监测结果的可视化方法
  4. 拙作《互联网时代的软件革命--SaaS架构设计》上市了
  5. shell编程-06-判断语句和循环语句
  6. 影响几代人的《芳华》,是如何创造14亿票房的?
  7. 俄罗斯主权因特网RuNet的特点
  8. Kali Linux 学习日记6 - 局域网攻击
  9. 为理想而超越理想,Bespin Global的崛起之路
  10. NYoj88 汉诺塔(一)