【问题标题】:How to rewrite a recursive function to use a loop instead?如何重写递归函数以使用循环?
【发布时间】:2011-07-16 20:37:53
【问题描述】:

这个堆栈溢出线程声称每个递归函数都可以写成一个循环。

Which recursive functions cannot be rewritten using loops?

这完全有道理。但是我不确定如何将以下递归函数表示为循环,因为它有一个前递归逻辑和一个后递归逻辑。

显然该解决方案不能使用 goto 语句。代码在这里:

def gen_perms(lst, k, m):

    if k == m:
        all_perms.append(list(lst))
    else:
        for i in xrange(k, m+1):

            #swap char
            tmp = lst[k]
            lst[k] = lst[i]
            lst[i] = tmp

            gen_perms(lst, k+1, m)

            #swap char
            tmp = lst[k]
            lst[k] = lst[i]
            lst[i] = tmp

调用它是这样的:

all_perms = []
gen_perm([1, 2, 3], 0, 2)

它会生成列表 1、2、3 的每个排列。

【问题讨论】:

  • @nmichaels:Python 没有 goto 语句。
  • 个人......我认为它可以解决,但你需要引用一些变量来管理你的递归是在循环内执行的事实......因此你需要存储循环位置和你通过递归的方向......可以说是“进入”或“离开”。
  • 是的,您可以使用相同的算法并假装自己是解释器并管理您自己的堆栈。或者,您可以提出一种迭代算法来生成排列,这将更容易表示为循环。
  • 您有这样做的理由吗?这纯粹是教育吗?任何递归函数都可以在没有递归的情况下实现(因为最终递归迭代),但并不是每个递归算法都应该迭代地实现。递归是表达许多算法的自然方式。
  • @Glenn Maynard:我正在尝试用 C 语言编写一个程序,以解决以前未解决的特定问题(用于流行的电视游戏节目)。我相信我可以使用蛮力方法解决问题,所以我想看看我是否能做到! C没有堆栈限制吗?我的搜索空间很大!

标签: python loops recursion permutation


【解决方案1】:

我对 python 语法不太熟悉,但是假设 python 可以嵌套 for 语句,以下代码(在“c”中)应该不难翻译。

int list[3]={1,2,3};
int i,j,k;

for(i=0;i < SIZE;i++)
for(j=0;j < SIZE;j++)
for(k=0;k < SIZE;k++)
if(i!=j && j!=k && i!=k)
printf("%d%d%d\n",list[i],list[j],list[k]);

【讨论】:

  • 这是否有效,因为每个数字都有一个索引....即如果列表大小为 4,我需要另一个循环吗?
  • 是的,这只有在您的列表大小为 3 时才有效。对于这么晚的评论,我深表歉意。
【解决方案2】:

最pythonic的排列方式是使用:

>>> from itertools import permutations
>>> permutations([1,2,3])
>>> list(permutations([1,2,3]))
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

【讨论】:

【解决方案3】:

假设您要查找 [1, 2, 3, 4] 的所有排列。其中有 24 个(=4!),因此将它们编号为 0-23。我们想要的是一种非递归的方式来找到第 N 个排列。

假设我们以递增的数字顺序对排列进行排序。那么:

  • 排列 0-5 从 1 开始
  • 排列 6-11 从 2 开始
  • 排列 12-17 从 3 开始
  • 排列 18-23 以 4 开头

所以我们可以通过将 N 除以 6 (=3!) 并四舍五入得到第一个排列数 N。

我们如何得到下一个数字?查看排列 0-5 中的第二个数字:

  • 排列 0-1 的第二个数字是 2。
  • 排列 2-3 的第二个数字是 3。
  • 排列 4-5 的第二个数字是 4。

我们在排列 6-11 中看到了类似的情况:

  • 排列 6-7 的第二个数字是 1。
  • 排列 8-9 的第二个数字是 3。
  • 排列 10-11 的第二个数字是 4。

一般来说,先取除以 6 后的余数,再除以 2 (=2!),然后四舍五入。这给了你 1、2 或 3,第二项是列表中剩下的第 1、2 或第 3 项(在你取出第一项之后)。

你可以这样继续下去。以下是执行此操作的一些代码:

from math import factorial

def gen_perms(lst):
    all_perms = []

    # Find the number of permutations.
    num_perms = factorial(len(lst))

    for i in range(num_perms):
        # Generate the ith permutation.
        perm = []
        remainder = i

        # Clone the list so we can remove items from it as we
        # add them to our permutation.
        items = lst[:]

        # Pick out each item in turn.
        for j in range(len(lst) - 1):
            # Divide the remainder at the previous step by the
            # next factorial down, to get the item number.
            divisor = factorial(len(lst) - j - 1)
            item_num = remainder / divisor

            # Add the item to the permutation, and remove it
            # from the list of available items.
            perm.append(items[item_num])
            items.remove(items[item_num])

            # Take the remainder for the next step.
            remainder = remainder % divisor

        # Only one item left - add it to the permutation.
        perm.append(items[0])

        # Add the permutation to the list.
        all_perms.append(perm)

    return all_perms

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 2010-10-06
    • 2018-08-23
    • 1970-01-01
    • 2020-08-02
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多