我的问题是关于python在递归工作时正在做什么。我得到了这个概念,但似乎循环中的显式是在递归算法中隐含的。我已经看到了递归循环的例子,然后再回过头来得到答案。我没有得到。这就像代码正在发生,我没有写。
我无法帮助'看到'返回语句返回一个等式,而不是构建一个等式并返回答案。
有一些递归的例子才有意义,但斐波那契和因子类型算法令人困惑。 (免责声明:我不想在斐波那契或因子中上课^ _ ^。)
def main():
num = int(input("Please enter a non-negative integer.\n"))
fact = factorial(num)
print("The factorial of",num,"is",fact)
def factorial(num):
if num == 0:
return 1
else:
return num * factorial(num - 1)
main()
如果我们这样做!10我不禁想到它应该返回每个方程式的结果并循环。我不确定python是如何通过内存工作的。或者它如何知道它需要返回10 * 9 * 8 * 7 * 6 ......等的值
而不是返回 返回10 *(10 - 1) 返回9 *(9 - 1) 返回8 *(8 - 1)
我知道返回会调用函数,因此无法返回任何内容......但是如果没有覆盖变量并丢失它的位置,它对已经找到的值有什么影响呢?
它是直接盯着我还是有些我不知道的东西?
答案 0 :(得分:3)
将其视为数学问题。如果你知道!9
的答案,你会如何计算!10
?您只需将!9
的值乘以10即可。
这正是递归函数正在做的事情;它只是表示num
的阶乘与num
乘以num - 1
的阶乘相同。唯一不起作用的数字是0
,但0
的阶乘是已知的,它是1
。
因此,10的阶乘基本上是:
10 * factorial(9) ==
10 * 9 * factorial(8) ==
10 * 9 * 8 * factorial(7) ==
10 * 9 * 8 * 7 * factorial(6) ==
10 * 9 * 8 * 7 * 6 * factorial(5) ==
10 * 9 * 8 * 7 * 6 * 5 * factorial(4) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * factorial(3) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * factorial(2) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * factorial(1) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * factorial(0) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * 1
请注意,每次factorial()
次调用都会获得新的变量集。没有覆盖;这是一个全新的,新的功能调用。每次调用中num
的值完全独立于函数的所有其他调用。
如果有帮助,请尝试使用笔记本跟踪功能信息。记下页面上的变量,在手动单步执行代码时更新它们。对于每个新的函数调用,翻过页面并从下一张纸开始,在那里写下变量。你在第一页上写10
,然后在第二页写9
(num - 1
)等等。返回意味着获取返回的值,从笔记本中删除页面并返回笔记本中的一个页面,用该返回值更新那里的变量。
Python完全相同,使用frame
个对象来跟踪变量。每个函数调用都是一个新帧,并在函数返回时再次丢弃帧。该调用的所有变量都随框架一起消失。
此外,Python不会关注您在此处重复使用相同的功能。您可以创建11个单独的函数,每个函数都有一个单独的名称和一个单独的num
名称:
def factorial10(num10):
if num10 == 0:
return 1
else:
return num10 * factorial9(num10 - 1)
def factorial9(num9):
if num9 == 0:
return 1
else:
return num9 * factorial8(num9 - 1)
def factorial8(num8):
if num8 == 0:
return 1
else:
return num8 * factorial7(num8 - 1)
# ...
# etc. all the way to
def factorial0(num0):
if num0 == 0:
return 1
else:
return num0 * factorialminus1(num0 - 1)
并且Python看不出这些函数和原始函数之间的任何差异。将执行完全相同的工作,但不是重复使用相同的功能,而是使用具有相同行为的不同功能对象。只有名字改变了。
因此,递归只是将一系列函数调用链接在一起的一种聪明方式。这些函数调用都是独立的,它们不关心其他函数的局部变量在做什么。 Python不必“知道”任何东西,只需要为你执行函数,当它遇到另一个函数调用时,执行该函数调用并使用返回值。该功能是相同的功能或不同的功能没有任何区别。
答案 1 :(得分:0)
这是一个很难以完全权威的方式回答的问题 - 我认为不同的人有不同的思考递归的方法。 我思考递归的方式是强迫自己以一种非常规范,抽象的方式思考一个函数是什么。函数只是一个映射。这在原则上很简单,但在实践中很容易忘记,特别是如果你习惯于以命令性的方式思考 - 也就是说,将程序视为一组指令。
暂时忘记说明,并以最抽象的形式思考阶乘函数:
X Y
--------------------
0 ------> 1
1 ------> 1
2 ------> 2
3 ------> 6
4 ------> 24
5 ------> 120
6 ------> 720
...
现在不要担心如何计算这个。想想抽象映射。现在让我们考虑如何制作这个函数的递归版本。我们需要什么?好吧,我们需要一个创建不同映射的函数 - 不是从[1, 2, 3, ...]
到阶乘的映射,而是从Y
的一个值到下一个值的映射。换句话说(现在使用小写):
x y
--------------------
1 ------> 1
1 ------> 2
2 ------> 6
6 ------> 24
24 ------> 120
120 ------> 720
720 ------> 5040
...
现在让我们考虑如何计算 this 。立即出现问题:1
第一次映射到1
,第二次映射到2
。所以我们知道我们将不得不写一个特殊情况来区分这两者。但对于其他人来说,这很简单,对吧?只需将x
乘以其在列表中的位置即可。所以这意味着对于映射的所有部分,我们只需要知道两件事:x
和列表中的position
:
def factorial_recurrence(x, position):
return x * position
请注意,此函数现在有两个参数,因此它实际上与上面的函数略有不同:
x, p y
------------------------
1 0 ------> 1
1 1 ------> 2
2 2 ------> 6
6 3 ------> 24
24 4 ------> 120
120 5 ------> 720
720 6 ------> 5040
这清楚地表明我们如何区分1
的两个映射。现在我们只需要想出一个获取位置信息的方法。恰好position
与X
的值相同。所以一种简单的方法是使用循环。在这里,我们只需将X == 0
设置为x
并在1
而不是1
开始循环来处理0
:
def factorial(X):
x = 1
for position in range(1, X + 1):
x = factorial_recurrence(x, position)
return x
现在请注意,此处的x
值已传递到factorial_recurrence
,然后结果将保存为x
。
所以这里真正发生的是函数的输出被传递回函数。这是一个重要的揭示:
那是递归!
在关键意义上,这已经是一种递归算法。这只是表示在里面 - 这里,并且函数还包含来自递归过程外部的position
信息。看看我的意思,看看这个:
def even_factorial(X):
x = 1
for position in range(2, X + 1, 2):
x = factorial_recurrence(factorial_recurrence(x, position - 1), position)
return x
对于factorial
的每个偶数值,这与X
的结果相同。 (它为X - 1
的奇数值提供X
的结果。)我们不必停在那里。我们可以为X
的每个第三个值做同样的事情(为了清晰起见,打破嵌套):
def third_factorial(X):
x = 1
for position in range(3, X + 1, 3):
x = factorial_recurrence(
factorial_recurrence(
factorial_recurrence(
x,
position - 2
),
position - 1
),
position
)
return x
现在每4日,每5日都做同样的事情,依此类推。如果您继续此过程,那么对于任何给定的X
,您最终都会创建一个函数,除了1
之外只返回X
,然后当您通过X
时你会得到X
的阶乘。
此时,递归技巧只是意识到我们可以通过factorial
调用自身来自动完成将循环内部转出的过程。每次调用factorial
时,它只会在最后一个内嵌另一个factorial_recurrence
调用 - 除非X
为0,在这种情况下,它返回1
,终止序列嵌套调用。
def factorial(X):
if X == 0:
return 1
else:
return factorial_recurrence(factorial(X - 1), X)
所以这是一种考虑递归的复杂方法,但它的价值在于它非常清楚地表明了递归函数的抽象与命令式代码中的具体实现之间的关系。