使递归函数迭代?

时间:2014-12-21 04:32:38

标签: python python-3.x recursion python-idle tail-recursion

当我运行以下模块时,它会运行大约960次递归:

import matplotlib
import pylab

xStart = 1
debug = 'off'
xList = []
calcList = []

def collatzCalc(xStart,calcs):
    calc = 0
    xCalc = 0
    xCalc = xStart
    while xCalc > 0:
        if debug == 'on':
            print(round(xCalc))
            print(xList)
            print(calcList)

        if xCalc == 1:
            xList.append(xStart)
            calcList.append(calc)
            xStart += 1
            if debug == 'on':
                print(calcs)
                print('---------------------------')

            calcs += 1
            collatzCalc(xStart,calcs)
        else:
            if xCalc % 2 == 0:
                xCalc = xCalc / 2
                calc += 1
            else:
                xCalc = xCalc * 3 + 1
                calc += 1

calcs = 0
collatzCalc(xStart,calcs)

它会抛出以下错误:

Traceback (most recent call last):
  File "C:\Users\Erin Lynch\Desktop\collatzConjecture.py", line 49, in <module>
    collatzCalc(xStart,calcs)
File "C:\Users\Erin Lynch\Desktop\collatzConjecture.py", line 32, in collatzCalc
    collatzCalc(xStart,calcs)
  File "C:\Users\Erin Lynch\Desktop\collatzConjecture.py", line 14, in collatzCalc
    while xCalc > 0:
RuntimeError: maximum recursion depth exceeded in comparison

我知道为什么会发生这种情况,因为我今天读到了递归限制,但我想知道的是我如何将递归公式转换为迭代公式。我完全迷失了如何做到这一点,我需要知道如何做的人的帮助。

1 个答案:

答案 0 :(得分:3)

首先,在这部分中,第一行是不必要的,因为xCalc会立即被xStart覆盖:

    xCalc = 0
    xCalc = xStart

其次,如果您仔细观察代码,您会发现如果xCalc到达1,它将永远循环:

def collatzCalc(xStart,calcs):
    ...
    xCalc = xStart
    while xCalc > 0:
        ...
        if xCalc == 1:
            ...
            collatzCalc(xStart,calcs)
        else:
            ...

由于xCalc本地变量collatzCalc的其他实例无法修改此变量。该函数将永远保持循环。虽然有必要永远循环在&#34;最外面的&#34;因为您正在检查Collat​​z猜想,所以递归地这样做是没有意义的。

我认为这是因为你混淆了这个程序的两个不同部分:

  1. 您想检查每个自然数的Collat​​z猜想。
  2. 您想要计算Collat​​z序列。
  3. 让我们解决第一个因为它更容易。要检查Collat​​z猜想,您只需要一个简单的循环:

    def checkCollatz(xStart=1, debug=False):
        xList = []
        calcList = []
        while True:
            calc = collatzCalc(xStart, debug=debug)
            xList.append(xStart)
            calcList.append(calc)
            if debug:
                print(xStart - 1)
                print('---------------------------')
            xStart += 1
    

    在这里,我已将全局变量(xListcalcListxStartdebug)转换为此最外层函数的局部变量。 (您可能还想为它们选择更多描述性名称。)请注意,我已完全删除了calcs变量,因为它似乎与xStart相同,但总是低一个。

    现在,要计算Collat​​z序列,我可以重用已有的(删除checkCollatz中使用的部分):

    def collatzCalc(xCalc, debug=False):
        calc = 0
        while xCalc > 0:
            if debug:
                print(xCalc)
            if xCalc == 1:
                return calc
            else:
                if xCalc % 2 == 0:
                    xCalc = xCalc // 2
                else:
                    xCalc = xCalc * 3 + 1
                calc += 1
    

    由于Collat​​z序列始终是整数,因此不需要使用round。此外,应使用整数除法(//)而不是浮点除法(/),因为/会将您的数字强制转换为浮点数。

    请注意,此代码中根本没有递归。递归已被消除并转换为checkCollatz中的循环(注意现在有两个 while循环而不是一个循环。你的原始代码已经基本上是迭代的,所以把它转换成一个递归代码并没有很大的参与。

    作为旁注,请注意,通过将函数拆分为两个单独的函数,现在变量更少,代码更容易阅读。