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

  查尔斯·巴贝奇是一名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. 定义上的区别 图灵机是一种抽象的计算设备,用于描述算法和解决问题的能力。它的核心在于通过有限的状态集合以及无限长的纸带来实现对输入字符串的操作[^3]。而递归模型则基于数学函数的概念,主要研究如何利用已知函数来构建更复杂的函数。递归模型通常涉及一般递归函数(General Recursive Functions),这些函数可以通过一组固定的规则逐步展开并最终得到结果。 尽管两者看似不同,但实际上它们具有相同的表达能力——即能够表示所有可计算函数。这一结论由阿隆佐·丘奇(Alonzo Church)和艾伦·图灵(Alan Turing)共同证明:一个函数如果是λ-computable,则必然也是Turing computable,并且还是general recursive[^2]。 #### 2. 应用场景差异 在实际应用过程中,由于图灵机更加贴近具体操作过程,因此常被用来作为理解复杂度理论的基础工具之一,在讨论时间空间资源消耗等方面发挥重要作用;相比之下,递归模型因其简洁明了的特点更适合应用于形式验证领域或者某些特定类型的优化问题之中。 #### 3. 抽象层次对比 从抽象程度来看,“图灵机”虽然被称为一种理想化的机器装置,但它并未真正考虑到物理层面的具体实现细节如硬件状态等因素,而是聚焦于逻辑架构的设计之上。与此相对应的是,递归模型往往不需要依赖具体的执行环境就能完成定义,这使得它能够在更高的层面上探讨各种可能性而不受制于底层约束条件的影响。 #### 4. 对程序设计语言发展的作用 值得注意的是,无论是哪种模型都极大地推动了现代计算机科学的发展进程。例如,基于图灵机的思想衍生出了许多重要的编程范式和技术手段,比如过程导向型或命令驱动式的开发方式等。与此同时,递归技术也广泛存在于众多高级脚本当中成为不可或缺的一部分。 ```python def factorial_recursive(n): """A simple example of a function using recursion.""" if n == 0 or n == 1: return 1 else: return n * factorial_recursive(n - 1) print(factorial_recursive(5)) # Output will be 120. ``` 上述代码片段展示了阶乘运算的一种典型递归解决方案,体现了递归模型的实际运用价值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值