递归与无穷大有关。我知道递归与无穷大有关。我想我知道递归与无穷大有关。他确信我认为我知道递归与无穷大有关。我们怀疑他是否确定我认为我知道......
我们认为,你认为,我们现在说服了你,我们可以永远继续这个自然语言递归的例子。递归不仅是自然语言的一个基本特征,也是人类认知能力的一个基本特征。我们的思维方式是基于递归的思维过程。即使使用非常简单的语法规则,例如“英语句子包含主语和谓语,谓语包含动词、宾语和补语”,我们也可以展示自然语言的无限可能。
认知科学家和语言学家斯蒂芬·平克 (Stephen Pinker) 是这样描述的:“有几千个名词可以填满主语,几千个动词可以填满谓语,人们已经有几百万种打开句子的方式。可能的组合很快地乘以难以想象的大数。事实上,句子的全部内容在理论上是无限的,因为语言规则使用了一种叫做递归的技巧。
递归规则允许一个短语包含自身的一个例子,如她认为他认为他们认为他们认为他知道等等,无穷无尽。如果句子的数量是无限的,那么可能的想法和意图的数量也是无限的,因为实际上每个句子都表达了不同的想法或意图。”1
我们必须停止在自然语言中使用递归的短途旅行,回到计算机科学和程序中的递归,最后回到编程语言 Python 中的递归。
形容词“递归”源于拉丁语动词“recurrere”,意思是“跑回去”。这就是递归定义或递归函数所做的:它在“跑回”或返回到自身。大多数学过数学、计算机科学或阅读有关编程的书的人都会遇到阶乘,它在数学术语中定义为
啊!= n * (n-1)!,如果 n > 1 且 0!= 1
由于其简单明了,它经常被用作递归的示例。
递归的定义递归是一种对问题进行编程或编码的方法,其中函数在其主体中一次或多次调用自身。通常,它返回此函数调用的返回值。如果一个函数定义满足递归条件,我们称这个函数为递归函数。
终止条件:递归函数必须满足一个重要的条件才能在程序中使用:它必须终止。如果每次递归调用问题的解决方案都缩小并朝着基本情况移动,则递归函数将终止。基本情况是指无需进一步递归即可解决问题的情况。如果调用中没有满足基本情况,递归可能会以无限循环结束。
例子:
4!= 4 * 3!
3!= 3 * 2!
2!= 2 * 1
替换计算值给了我们以下表达式
4!= 4 * 3 * 2 * 1
换句话说,计算机科学中的递归是一种解决问题的方法,它基于解决同一问题的较小实例。
Python 中的递归函数现在我们来在 Python 中实现阶乘。它就像数学定义一样简单而优雅。
def factorial ( n ):
if n == 0 :
return 1
else :
return n * factorial ( n - 1 )
我们可以通过在前面的函数定义中添加两个 print() 函数来跟踪该函数的工作方式:
def factorial ( n ):
print ( "factorial has been called with n = " str ( n ))
if n == 1 :
return 1
else :
res = n * factorial ( n - 1 )
print ( "intermediate result for " , n , " * factorial(" , n - 1 , "): " , res )
返回 res
打印(阶乘(5 ))
阶乘已被调用,n = 5
阶乘已被调用,n = 4
阶乘已被调用,n = 3
阶乘已被调用,n = 2
阶乘已被调用,n = 1
2 * factorial( 1 ) 的中间结果:2
3 * factorial( 2 ) 的中间结果:6
4 * factorial( 3 ) 的中间结果:24
5 * factorial( 4 ) 的中间结果:120
120
让我们看一下阶乘函数的迭代版本。
def iterative_factorial ( n ):
result = 1
for i in range ( 2 , n 1 ):
result *= i
return result
对于 我 在 范围(5 ):
打印(我, iterative_factorial (我))
0 1
1 1
2 2
3 6
4 24
通常的做法是将 0 的阶乘函数扩展为参数。定义0!为 是有道理的1,因为只有零个对象的一个排列,即如果没有什么要排列,“一切”就留在原地。另一个原因是在一组 n 中选择 n 个元素的方法的数量计算为 n!除以 n! 的乘积!和0!。
为了实现这一点,我们所要做的就是改变 if 语句的条件:
def factorial ( n ):
if n == 0 :
return 1
else :
return n * factorial ( n - 1 )
我们关于递归的论文现在将我们引向另一个有趣的递归案例。
向日葵、黄金比例、枞树球果、达芬奇密码、Tool 的歌曲“Lateralus”和右侧的图形有什么共同点?对,斐波那契数列。我们不介绍斐波那契数列,因为它们是递归函数的另一个有用示例。尝试为这个数字序列编写递归函数可能会导致程序效率低下。事实上,如此低效以至于它不会有用。我们介绍斐波那契数列是为了向您展示递归的陷阱。
斐波那契数是以下整数值序列的数字:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
斐波那契数列定义为:
Fn=Fn- Fn-2
F0=0 和 F1=1F0=0 和 F1=1
斐波那契数列以比萨的数学家列奥纳多 (Leonardo of Pisa) 的名字命名,他更为人所知的是斐波那契。在他的书“Liber Abaci”(出版于 1202 年)中,他介绍了这个序列作为处理兔子的练习。他的斐波那契数列从 F1 = 1 开始,而在现代数学中,该序列从 F0 = 0 开始。但这对序列的其他成员没有影响。
斐波那契数是人工兔子种群的结果,满足以下条件:
- 一对刚出生的兔子,一公一母,建立初始种群
- 这些兔子可以在一个月大的时候交配,这样在第二个月末,雌性就可以生育另一对兔子
- 这些兔子是不朽的
- 从第二个月开始,一对交配的一对总是每个月产生一对新的(一男一女)
斐波那契数是n几个月后兔子对的数量,即 10 个月后我们将拥有 F10 兔子。F10兔子。
斐波那契数列很容易写成 Python 函数。它或多或少是数学定义中的一对一映射:
def fib ( n ):
if n == 0 :
return 0
elif n == 1 :
return 1
else :
return fib ( n - 1 ) fib ( n - 2 )
迭代解决方案也很容易编写,尽管递归解决方案看起来更像定义:
def fibi ( n ):
old , new = 0 , 1
if n == 0 :
return 0
for i in range ( n - 1 ):
old , new = new , old new
return new
我们将编写一个名称fibonacci包含函数fib和fibi. 为此,您必须将以下代码复制到名为 fibonacci0.py 的文件中:
""" 包含斐波那契函数的递归和迭代实现的
模块。该模块的目的在于展示斐波那契函数的纯递归实现的低效率!"""
def fib ( n ):
""" 斐波那契函数的递归版本 """
if n == 0 :
return 0
elif n == 1 :
return 1
else :
return fib ( n - 1 ) fib ( n - 2 )
def fibi ( n ):
""" 斐波那契函数的迭代版本 """
old , new = 0 , 1
if n == 0 :
return 0
for i in range ( n - 1 ):
old , new = new , old 新
返回 新
覆盖 fibonacci0.py
如果您检查函数 fib() 和 fibi(),您会发现迭代版本 fibi() 比递归版本 fib() 快很多。为了了解这“快得多”可以达到多少,我们编写了一个脚本,它使用 timeit 模块来测量调用。为此,我们将 fib 和 fibi 的函数定义保存在文件 fibonacci.py 中,我们可以在下面的程序 (fibonacci_runit.py) 中导入该文件:
从 timeit 导入 计时器
t1 = Timer ( "fib(10)" , "from fibonacci import fib" )
对于 我 在 范围(1 , 20 ):
CMD = “FIB(” STR (我) “)”
T1 = 定时器(CMD , “从进口斐波纳契FIB” )
TIME1 = T1 。timeit ( 3 )
cmd = "fibi(" str ( i ) ")"
t2 = Timer ( cmd , "from fibonacci import fibi" )
时间 2 = t2 。timeit ( 3 )
打印( f "n= { i : 2d } , fib: { time1 : 8.6f } , fibi: { time2 : 7.6f } , time1/time2: { time1 / time2 : 10.2f } " )
n= 1, fib: 0.000001, fibi: 0.000002, time1/time2: 0.50
n= 2, fib: 0.000002, fibi: 0.000002, time1/time2: 0.85
n= 3, fib: 0.000003, fibi: 0.000002, time1/time2: 1.11
n= 4, fib: 0.000004, fibi: 0.000003, time1/time2: 1.45
n= 5, fib: 0.000011, fibi: 0.000003, time1/time2: 3.35
n= 6, fib: 0.000008, fibi: 0.000003, time1/time2: 2.98
n= 7, fib: 0.000012, fibi: 0.000002, time1/time2: 5.05
n= 8, fib: 0.000018, fibi: 0.000002, time1/time2: 7.98
n= 9, fib: 0.000029, fibi: 0.000002, time1/time2: 12.15
n=10, fib: 0.000046, fibi: 0.000003, time1/time2: 18.18
n=11, fib: 0.000075, fibi: 0.000003, time1/time2: 28.42
n=12, fib: 0.000153, fibi: 0.000003, time1/time2: 51.99
n=13, fib: 0.000194, fibi: 0.000003, time1/time2: 72.11
n=14, fib: 0.000323, fibi: 0.000003, time1/time2: 109.50
n=15, fib: 0.001015, fibi: 0.000008, time1/time2: 134.65
n=16, fib: 0.002165, fibi: 0.000008, time1/time2: 261.11
n=17, fib: 0.003795, fibi: 0.000019, time1/time2: 198.87
n=18, fib: 0.006021, fibi: 0.000010, time1/time2: 574.59
n=19, fib: 0.008807, fibi: 0.000011, time1/time2: 785.54
time1是 3 次调用所需的时间(以秒为单位)fib(n),time2分别是fibi(n).
我们的递归实现有什么问题?
让我们来看看计算树,即调用函数的顺序。fib() 被 f() 代替。
我们可以看到子树 f(2) 出现了 3 次,而用于计算 f(3) 的子树出现了两次。如果你想象为 f(6) 扩展这棵树,你就会明白 f(4) 将被调用两次,f(3) 被调用三次,依此类推。这意味着,我们的递归不会记住先前计算的值。
我们可以通过使用字典来保存先前计算的值,为我们的递归版本实现“内存”。我们称这个版本为fibm:
备忘录 = { 0 :0 , 1 :1 }
DEF fibm (Ñ ):
如果 不 Ñ 在 备忘录:
备忘录[ Ñ ] = fibm (ñ - 1 ) fibm (ñ - 2 )
返回 备忘录[ Ñ ]
在我们对新版本做一些计时之前,我们将它添加到我们的fibonacci模块中:
""" 包含斐波那契函数的递归和迭代实现的
模块。该模块的目的在于展示斐波那契函数的纯递归实现的低效率!"""
def fib ( n ):
""" 斐波那契函数的递归版本 """
if n == 0 :
return 0
elif n == 1 :
return 1
else :
return fib ( n - 1 ) fib ( n - 2 )
def fibi ( n ):
""" 斐波那契函数的迭代版本 """
old , new = 0 , 1
if n == 0 :
return 0
for i in range ( n - 1 ):
old , new = new , old 新
返回 新
memo = { 0 : 0 , 1 : 1 }
def fibm ( n ):
""" 递归斐波那契函数,它
在字典 memo 的帮助下
记忆先前计算的值 """如果 在memo 中不是 n : memo [ n ] = fibm ( n - 1 ) fibm ( n - 2 )返回备忘录[ n ]
覆盖 fibonacci.py
我们再次计时以将其与 fibi() 进行比较:
from timeit import Timer
from fibonacci import fib
t1 = Timer ( "fib(10)" , "from fibonacci import fib" )
对于 我 在 范围(1 , 20 ):
小号 = “fibm(” STR (我) “)”
T1 = 定时器(小号,“从进口斐波纳契fibm” )
TIME1 = T1 。timeit ( 3 )
s = "fibi(" str ( i ) ")"
t2 = Timer ( s , "from fibonacci import fibi" )
时间 2 = t2 。timeit ( 3 )
打印( f "n= { i : 2d } , fibm: { time1 : 8.6f } , fibi: { time2 : 7.6f } , time1/time2: { time1 / time2 : 10.2f } " )
n= 1, fibm: 0.000002, fibi: 0.000002, time1/time2: 0.68
n= 2, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.63
n= 3, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.51
n= 4, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.46
n= 5, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.48
n= 6, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.41
n= 7, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.41
n= 8, fibm: 0.000001, fibi: 0.000011, time1/time2: 0.10
n= 9, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.40
n=10, fibm: 0.000001, fibi: 0.000002, time1/time2: 0.36
n=11, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.38
n=12, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.35
n=13, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.44
n=14, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.36
n=15, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.35
n=16, fibm: 0.000001, fibi: 0.000004, time1/time2: 0.34
n=17, fibm: 0.000001, fibi: 0.000003, time1/time2: 0.33
n=18, fibm: 0.000001, fibi: 0.000004, time1/time2: 0.29
n=19, fibm: 0.000001, fibi: 0.000004, time1/time2: 0.27
我们可以看到它甚至比迭代版本还要快。当然,论点越大,我们记忆的好处就越大:
我们还可以通过使用具有 callabe 实例的类,即使用特殊方法call为我们的 Fibonacci 函数定义递归算法。这样,我们将能够以优雅的方式隐藏字典。我们使用了一种通用方法,它允许定义类似于 Fibonacci 的函数,如 Lucas 函数。这意味着我们可以通过实例化此类的实例来创建类似斐波那契数列的数字序列。基本上,构建原理,即前两个数字的总和,将是相同的。它们将因两个初始值而异:
FibonacciLike类:
def __init__ ( self , i1 = 0 , i2 = 1 ):
self 。备忘录 = { 0 : i1 , 1 : i2 }
高清 __call__ (自我, ñ ):
如果 ñ 不是 在 自我。备忘录:
自我。备忘录[ n ] = 自我。__call__ ( n - 1 ) self 。__call__ ( n - 2 )
返回 self 。备忘录[ n ]
# 我们创建一个可调用对象来创建斐波那契数列:
fib = FibonacciLike ()
# 这将创建一个可调用对象来创建 Lucas 数列:
lucas = FibonacciLike ( 2 , 1 )
对于 我 在 范围(1 , 16 ):
打印(我, FIB (我), 卢卡斯(我))
1 1 1
2 1 3
3 2 4
4 3 7
5 5 11
6 8 18
7 13 29
8 21 47
9 34 76
10 55 123
11 89 199
12 144 322
13 233 521
14 377 843
15 610 1364
卢卡斯数或卢卡斯级数是一个整数序列,以数学家弗朗索瓦·爱德华·阿纳托尔·卢卡斯 (1842-91) 的名字命名,他研究了该序列和密切相关的斐波那契数。卢卡斯数与斐波那契数有相同的创生法则,即前两个数之和,但0和1的取值不同。
广义斐波那契数列现在我们将证明我们的方法也适用于计算任意广义斐波那契数列。斐波那契数列取决于前面的两个值。我们现在通过以下方式定义 k-Fib 序列来概括这个概念
给定一个固定的自然数k和k >= 2。还给出了k初始值一世0,一世1,…一世克-1
满意 F克(0)=一世0,…F克(克-1)=一世克-1
该功能还需要 克 系数 C0,C1,…C克-1
F克(n) 被定义为
F克(n)=一个0⋅F克(n-1) 一个1⋅F克(n-2)…一个克-1⋅F克(n-克)
和 克<=n
kFibonacci类:
def __init__ ( self , k , initials , coefficients ):
self . memo = dict ( zip ( range ( k ), initials ))
self . coeffs = 系数
self 。k = k
def __call__ ( self , n ):
k = self 。ķ
如果 ñ 不是 在 自我。备忘录:
结果 = 0
为 系数_ , 我 在 拉链(自我。coeffs , 范围(1 , ķ 1 )):
结果 = 系数_ * 自我。__call__ ( n - i )
self. 备注[ n ] = 结果
返回 self 。备忘录[ n ]
fib = kFibonacci ( 2 , ( 0 , 1 ), ( 1 , 1 ))
lucas = kFibonacci ( 2 , ( 2 , 1 ), ( 1 , 1 ))
对于 我 在 范围(1 , 16 ):
打印(我, FIB (我), 卢卡斯(我))
1 1 1
2 1 3
3 2 4
4 3 7
5 5 11
6 8 18
7 13 29
8 21 47
9 34 76
10 55 123
11 89 199
12 144 322
13 233 521
14 377 843
15 610 1364
Pell 数字的序列以 1, 2, 5, 12, 29, ...
功能是
磷(n)=2⋅磷(n-1) 磷(n-2) 和 磷(0)=1 和 磷(1)=1
用我们的kFibonacci类很容易制定这个序列:
P = kFibonacci (2 , (1 , 2 ), (2 , 1 ))
为 我 在 范围(10 ):
print ( i , P ( i ))
0 1
1 2
2 5
3 12
4 29
5 70
6 169
7 408
8 985
9 2378
我们可以通过在两个佩尔数 P(i) 和 P(i 1) 之间添加前两个佩尔数之和来创建另一个有趣的系列。我们得到:
磷(0),磷(1),磷(0) 磷(1),磷(2),磷(1) 磷(2),磷(3),磷(2) 磷(3),…
对应于: 1,2,3,5,7,12,17,29,…
def nP ( n ):
如果 n < 2 :
return P ( n )
else :
i = n // 2 1
if n % 2 : # n 是奇数
return P ( i )
else :
return P ( i - 1 ) P ( i - 2 )
对于 我 在 范围(20 ):
打印(为nP (我), 结束= “” )
1, 2, 3, 5, 7, 12, 17, 29, 41, 70, 99, 169, 239, 408, 577, 985, 1393, 2378, 3363, 5741,
如果你仔细观察这些数字,你会发现在这个序列中还有另一个规则。我们可以将 nP 定义为
n磷(n)=2⋅n磷(n-2) n磷(n-4) 和 n>3 和起始值 1,2,3,5
这是我们kFibonacci班级的另一个简单应用:
nP = kFibonacci ( 4 , ( 1 , 2 , 3 , 5 ), ( 0 , 2 , 0 , 1 ))
for i in range ( 20 ):
print ( nP ( i ), end = ", " )
1, 2, 3, 5, 7, 12, 17, 29, 41, 70, 99, 169, 239, 408, 577, 985, 1393, 2378, 3363, 5741,
一个有趣的数学事实:对于每个奇数 n 的商 磷(n) 经过 磷(n-1) 是一个近似值 2. 如果n是偶数,商是一个近似值1 12
sqrt2 = 2 ** 0.5
print ( "2的平方根:" , sqrt2 )
print ( "3的平方根:" , 1 1 / sqrt2 )
for i in range ( 1 , 20 ):
print ( nP ( i ) / nP ( i - 1 ), end = ", " )
2 的平方根:1.4142135623730951
3 的平方根:1.7071067811865475
2.0,1.5,1.6666666666666667,1.4,1.7142857142857142,1.4166666666666667,1.7058823529411764,1.4137931034482758,1.7073170731707317,1.4142857142857144,1.707070707070707,1.4142011834319526,1.707112970711297,1.4142156862745099,1.707105719237435,1.4142131979695431,1.7071069633883704,1.4142136248948696,1.7071067499256616,
如果您想了解更多关于递归的知识,我们建议您尝试解决以下练习。在你尽力而为之前,请不要盯着解决方案。如果您已经考虑了一段时间的任务,但仍然无法解决该练习,您可以参考我们的示例解决方案。
在我们教程的“高级主题”部分,我们对游戏或谜题“河内之塔”进行了全面的处理。当然,我们通过使用递归函数的函数来解决它。“河内问题”很特别,因为递归解决方案几乎将自己强加于程序员,而游戏的迭代解决方案很难找到和掌握。
练习练习 1想想函数 f(n) = 3 * n 的递归版本,即 3 的倍数
练习 2编写一个递归 Python 函数,返回前 n 个整数的总和。(提示:该函数将类似于阶乘函数!)
练习 3编写一个实现帕斯卡三角形的函数:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
斐波那契数列隐藏在帕斯卡三角形内。如果您将以下三角形的彩色数字相加,您将得到第 7 个斐波那契数:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
使用帕斯卡三角形编写一个递归程序来计算斐波那契数列。
练习 5在 Python 中为 Eratosthenes 的筛子实现递归函数。
Eratosthenes 筛法是一种简单的算法,用于查找指定整数以内的所有素数。它是由古希腊数学家埃拉托色尼创造的。求所有小于或等于给定整数n的素数的算法:
- 创建一个从 2 到 n 的整数列表:2, 3, 4, ..., n
- 从我设置为 2 的计数器开始,即第一个质数
- 从 i i 开始,按 i 递增并从列表中删除这些数字,即 2 i、3 i、4*i 等。
- 找出 i 后面的列表的第一个数字。这是下一个质数。
- 将 i 设置为在上一步中找到的数字
- 重复步骤 3 和 4,直到 i 大于 n。(作为改进:去n的平方根就够了)
- 所有仍在列表中的数字都是素数
你可以很容易地看到,如果我们严格使用这个算法,我们将是低效的,例如,我们将尝试去除 4 的倍数,尽管它们已经被 2 的倍数去除了。所以它足以产生所有的倍数直到 n 的平方根的素数。我们可以递归地创建这些集合。
练习 6写一个递归函数fib_indexfib(),返回斐波那契数列中某个数的索引,如果该数是该数列的元素,则返回-1,如果不包含该数,即
F一世乙(F一世乙_一世nd电子X(n))==n
练习 7两个连续斐波那契数的平方和也是斐波那契数,例如2和3是斐波那契数列的元素,2 2 3 3 = 13对应斐波那契(7)。 用前面的函数求位置斐波那契数列中两个连续数的平方和。
数学解释:设 a 和 b 是两个连续的斐波那契数,a 先于 b。以数字“a”开头的斐波那契数列如下所示:
0个
1个
2 a b
3 a 2b
4 2a 3b
5 3a 5b
6 5a 8b
我们可以看到斐波那契数列作为 a 和 b 的因子出现。此序列中的第 n 个元素可以使用以下公式计算:
F(n)=F一世乙(n-1)⋅一个 F一世乙(n)⋅乙
由此我们可以得出结论,对于自然数 n,n>1,以下成立:
F一世乙(2⋅n 1)=F一世乙(n)2 F一世乙(n 1)2
练习 8的tribonacci号码是像斐波那契数,但不是与两个预定条件开始,该序列与三个预定术语开始的,每个术语之后是前述三项的总和。前几个 tribonacci 数是:
0, 0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513, 356812, ...
所述tetranacci号码与四个预定术语开始,每个术语之后是前述4项的总和。前几个四连数是:
0, 0, 0, 1, 1, 2, 4, 8, 15, 29, 56, 108, 208, 401, 773, 1490, 2872, 5536, 10671, 20569, 39648, ...
对于pentanacci、hexanacci、heptanacci、octanacci 等,这以相同的方式继续。
为 tribonacci 和tetranacci 数编写函数。
练习 9:如果有人想检查递归函数中的参数怎么办?例如阶乘函数。好的,请编写一个递归版本的 factorial 来检查参数。也许你以完美的方式做到了,但也许不是。你能改进你的版本吗?摆脱不必要的测试?
解决方案练习 1 的解决方案在数学上,我们可以这样写:
F(1)=3,
F(n 1)=F(n) 3
一个 Python 函数可以这样写:
def mult3 ( n ):
如果 n == 1 :
return 3
else :
return mult3 ( n - 1 ) 3
对于 我 在 范围(1 ,10 ):
打印(mult3 (我))
3
6
9
12
15
18
21
24
27
def sum_n ( n ):
if n == 0 :
return 0
else :
return n sum_n ( n - 1 )
def pascal ( n ):
if n == 1 :
return [ 1 ]
else :
line = [ 1 ]
previous_line = pascal ( n - 1 )
for i in range ( len ( previous_line ) - 1 ):
line 。追加(previous_line [ i ] previous_line [ i 1 ])
行 = [ 1 ]
返回 行
打印(帕斯卡(6 ))
[1, 5, 10, 10, 5, 1]
或者,我们可以使用列表推导编写一个函数:
def pascal ( n ):
if n == 1 :
return [ 1 ]
else :
p_line = pascal ( n - 1 )
line = [ p_line [ i ] p_line [ i 1 ] for i in range ( len ( p_line ) - 1 )]
行。插入( 0 , 1)
线。追加( 1 )
返回 行
打印(帕斯卡(6 ))
[1, 5, 10, 10, 5, 1]
从帕斯卡三角形中产生斐波那契数列:
def fib_pascal ( n , fib_pos ):
if n == 1 :
line = [ 1 ]
fib_sum = 1 if fib_pos == 0 else 0
else :
line = [ 1 ]
( previous_line , fib_sum ) = fib_pascal ( n - 1 , fib_pos 1 )
用于 我 在 范围(len个( previous_line ) - 1 ):
行。append ( previous_line [ i ] previous_line [ i 1 ])
line = [ 1 ]
if fib_pos < len ( line ):
fib_sum = line [ fib_pos ]
return ( line , fib_sum )
def fib ( n ):
返回 fib_pascal ( n , 0 )[ 1 ]
# 现在打印出前十个斐波那契数:
for i in range ( 1 , 10 ):
print ( fib ( i ))
1
1
2
3
5
8
13
21
34
下面的程序按照任务的规则以迭代的方式实现了埃拉托色尼筛法。它将打印出前 100 个质数。
from math import sqrt
def screen ( n ):
# 返回 2 和 n 之间的所有素
数 = list ( range ( 2 , n 1 ))
max = sqrt ( n )
num = 2
while num < max :
i = num
while i <= n :
i = num
if i in primes :
primes .删除( i )
for j in primes :
if j > num :
num = j
break
return primes
打印(筛(100 ))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ]
但是我们教程的这一章是关于递归和递归函数的,我们需要一个递归函数来计算素数。要理解以下解决方案,您可以参考我们关于列表理解的章节:
从 数学 导入 sqrt
def primes ( n ):
if n == 0 :
return []
elif n == 1 :
return []
else :
p = primes ( int ( sqrt ( n )))
no_p = [ j for i in p for j in range ( i * 2 , n 1 , i )]
p = x [ 为 X 在 范围(2 , Ñ 1 ) 如果 X 不 在 no_p ]
返回 p
打印(素数(100 ))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ]
备忘录 = { 0 :0 , 1 :1 }
DEF FIB (Ñ ):
如果 不 Ñ 在 备忘录:
备忘录[ Ñ ] = FIB (ñ - 1 ) FIB (ñ - 2 )
返回 备忘录[ Ñ ]
def fib_index ( * x ):
""" 用 fib(i) == n """ 查找自然数 i
if len ( x ) == 1 :
# 由用户开始
# 查找从 0
开始的索引return fib_index ( x [ 0 ], 0 )
else :
n = fib ( x [ 1 ])
m = x [ 0 ]
如果 n > m :
return - 1
elif n == m :
return x [ 1 ]
else :
return fib_index ( m , x [ 1 ] 1 )
# 上一示例中带有 fib() 和 find_index() 函数的代码
print ( " a | a | b | 平方和 | 索引" 的索引" )
print ( "============================== ========================” )
为 我 在 范围(15 ):
方形 = FIB (我)** 2 FIB (我 1 )** 2
打印( f " { i : 12d } | { fib ( i ) : 6d } |{fib ( i 1 ) : 9d } | {正方形:14d } | { fib_index ( square ) : 5d } " )
索引 | 一个| 乙 | 平方和 | 指数
================================================== ===
0| 0| 1| 1| 1
1| 1| 1| 2| 3
2| 1| 2| 5| 5
3| 2| 3| 13| 7
4| 3| 5| 34| 9
5| 5| 8| 89| 11
6| 8| 13| 233| 13
7| 13| 21| 610| 15
8| 21| 34| 1597| 17
9| 34| 55| 4181| 19
10| 55| 89| 10946| 21
11| 89| 144| 28657| 23
12| 144| 233| 75025| 25
13| 233| 377| 196418| 27
14| 377| 610| 514229| 29
我们将使用kFibonacci我们在本章中定义的类。
tribonacci = kFibonacci ( 3 , ( 0 , 0 , 1 ), ( 1 , 1 , 1 ))
对于 我 在 范围(20 ):
打印(tribonacci (我), 结束= “” )
0, 0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513,
四面体 = kFibonacci ( 4 , ( 0 , 0 , 0 , 1 ), ( 1 , 1 , 1 , 1 ))
对于 我 在 范围(20 ):
打印(tetranacci (我), 结束= “” )
0, 0, 0, 1, 1, 2, 4, 8, 15, 29, 56, 108, 208, 401, 773, 1490, 2872, 5536, 10671, 20569,
我们可以像这样轻松地创建它们:
前缀 = [ '摩擦' , '四' , '的Pentra' , '六' , '庚' , '八' ]
为 ķ , 前缀 在 拉链(范围(len个(前缀)), 前缀):
CMD = 前缀 “ nacci = kFibonacci("
cmd = str ( k 3 ) ", (" "0, " * ( k 2 ) "1), "
cmd = "(" "1, " * ( k 2 ) "1))"
打印( cmd )
exec ( cmd )
打印( " \n测试八角函数:" )
for i in range ( 20 ):
打印(八角( i ), end = ", " )
tribonacci = kFibonacci(3, (0, 0, 1), (1, 1, 1))
四面体 = kFibonacci(4, (0, 0, 0, 1), (1, 1, 1, 1))
pentranacci = kFibonacci(5, (0, 0, 0, 0, 1), (1, 1, 1, 1, 1))
六面体 = kFibonacci(6, (0, 0, 0, 0, 0, 1), (1, 1, 1, 1, 1, 1))
heptanacci = kFibonacci(7, (0, 0, 0, 0, 0, 0, 1), (1, 1, 1, 1, 1, 1, 1))
Octanacci = kFibonacci(8, (0, 0, 0, 0, 0, 0, 0, 1), (1, 1, 1, 1, 1, 1, 1, 1))
测试八角函数:
0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 32, 64, 128, 255, 509, 1016, 2028,
def factorial ( n ):
""" 计算 n 的阶乘,
n 应该是一个整数并且 n <= 0 """
if type ( n ) == int and n >= 0 :
if n == 0 :
return 1
否则:
返回 n * 阶乘( n - 1 )
其他:
加薪 类型错误(“N必须是一个正整数或零” )
你怎么看这个解决方案?factorial(10)例如你打电话。该函数将检查是否10为正整数。该函数将递归调用factorial(9). 在此调用中,函数将再次检查是否9为整数。还能是什么?第一次调用已检查10所有我们所做的就是减去1从10。
我们可以通过定义一个内部函数来解决这个问题:
def factorial ( n ):
""" 计算 n 的阶乘,
n 应该是一个整数并且 n <= 0 """
def inner_factorial ( n ):
if n == 0 :
return 1
else :
return n * inner_factorial ( n - 1 )
如果 类型( n ) == int 并且 n >= 0 :
返回 inner_factorial ( n )
else :
加注 类型错误(“n应为positve int或0” )
打印(阶乘(10 ))
3628800
测试只会在第一次调用时执行10!
*斯蒂芬平克,空白石板,2002
,