递归递减列表1

时间:2013-04-27 23:14:51

标签: python recursion

非常快速简单的家庭作业问题。我运行正常,但我认为有一个更好的 这样做的方式。更多的Pythonic方式。
这是我的代码递归递减列表的每个元素1.

l = range(30)  
def recurseDecrMap(l, x = []):  
    if len(l) == 0:  
        return []  
    else:  
        x.append(l[0] -1)  
    recurseDecrMap(l[1:], x)  
    return x  

非常感谢任何意见。我正在努力学习做更好的递归。遇到麻烦 它的诀窍。

7 个答案:

答案 0 :(得分:25)

可能 pythonic,但有:

def recurseDecrMap(l):
    return [l[0]-1] + recurseDecrMap(l[1:]) if l else []

答案 1 :(得分:14)

你只能使用一个参数,在我看来它更简单:

def recurseDecrMap(l):  
    if not l:  
        return []  
    else:
        return [l[0]-1] + recurseDecrMap(l[1:])

但正如@jamylak指出的那样,该算法的复杂性为O(N ^ 2),因为l[1:]创建了一个新列表,其中包含对列表中其余项的引用。

如果您需要效率,我建议您使用列表推导(Haidro's answer),但我认为如果您只是为了学习目的,它不是优先考虑的事项。

答案 2 :(得分:9)

对于它的价值,这是一种了解递归的可怕方法,因为你正在使用它来做一些本身不具有递归性的东西。如果你的老师真的要求你编写一个程序,递减递归列表的元素,如[1, 2, 3, 4] 递归,那么他/她就会感到羞耻。

正如Haidro所说,解决这个问题的最Pythonic方法是使用列表理解迭代列表

[i - 1 for i in l]

作为循环,这是

def decr(l):
    a = []
    for i in l:
        a.append(i - 1)
    return a

如果要在深度的任意级别解决相同的问题,递归很有用。例如,假设您有类似[1, [2, 3], [[4], 5]]的内容,并且希望将每个数字减1,同时保持列表结构。在这种情况下,递归解决方案将使用基本情况的迭代解决方案,并为递归情况调用自身。

def decr_recursive(l):
    a = []
    for i in l:
        if isinstance(i, list):
            a.append(decr_recursive(i))
        else:
            a.append(i - 1)
    return a

如果您想支持的不仅仅是列表或整数,可以轻松修改。

>>> decr([1, [2, 3], [[4], 5]])
[0, [1, 2], [[3], 4]]

这个是一种在不使用递归的情况下很难解决的问题,但很容易用它来解决。你要问的是没有递归就很容易解决的问题(为了上帝的缘故,这只是一个简单的迭代迭代),但有些难以解决。

为什么要避免Python中的递归

  • 阅读起来比较困难。将[i - 1 for i in l]或甚至显式循环与

    进行比较
    def decr(l):
        if not l:
            return []
        return [l[0] - 1] + decr(l[:1])
    
  • 在Python中调用函数可能很昂贵。我在电脑上和Ashwini Chaudhary大致相同。但[i - 1 for i in range(10**4)]在我的电脑上需要559μs。这比最快的递归方法快三个数量级。

  • 除非您将递归限制设置得更高,否则递归函数不会超过1000次调用。您可能已经注意到Ashwini Chaudhary的答案中的sys.setrecursionlimit(10**5)电话。这是必要的,因为没有它,每次调用都会导致RuntimeError: maximum recursion depth exceeded跟踪一个巨大的追溯。但即便如此,更大的列表仍然会导致递归限制。根据{{​​3}},每个操作系统都有一个上限,设置得太高会导致崩溃。

  • 递归函数更难调试。它们不仅会使用来自同一函数的数百个调用来污染堆栈跟踪,但它们在概念上更难以遵循,因为同一函数的相同部分以不同的方式使用,具体取决于您在堆栈中的哪个级别人类自然的思维方式是迭代的。我们一次做一件事。我们自己的大脑“堆栈”只有几层深,所以我们很难以递归方式解决问题,比如“让我开始解决问题,但在我完成之前,让我解决另一个问题,然后当我完成后,我将完成原来的问题。在较小的问题中,我可能会做同样的事情,所以在我完成之前我会得到几个等级。“这就是为什么你走进厨房拿笔,然后你看到一个糖果棒开始吃它,当你完成后,你忘记了笔。你“递归”了一个级别,从笔问题到直板问题,​​你的心理堆栈太深了(只有两个级别,但这就足够了)。如果你反而抓住了糖果棒,但是在你打开它并开始吃它之前,还找到了笔(我能想出的最佳迭代方法),你可以做到这两点而不会忘记。解决程序问题的方式应该与解决问题的方式完全相同,因为这是了解代码执行情况的唯一方法。 Python是一种非常好的语言,因为它的高级接口让你可以做到这一点(至少比在低级语言中更常见)。 使用这个事实!

答案 3 :(得分:6)

这是最糟糕的方式 - 使用Fixed Point Combinator

Y = lambda g: (lambda f: g(lambda arg: f(f)(arg))) (lambda f: g(lambda arg: f(f)(arg)))
recurseDecrMap = Y(lambda f: lambda l: [l[0]-1] + f(l[1:]) if l else [])

答案 4 :(得分:2)

这是一种简单的pythonic方式:

>>> mylist = range(30)
>>> def func(l):
...     return [i-1 for i in l]
>>> func(mylist)
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]

说明:

我使用list comprehensionsmylist中的每个元素创建了一个新列表,其中的值比它的值小1。

您的代码没有任何问题,除非您不止一次使用它:

>>> recurseDecrMap(l)
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]

>>> recurseDecrMap(l)
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]

要避免这种情况,请查看this answer

答案 5 :(得分:2)

比较各种方法:

In [1]: import sys

In [2]: sys.setrecursionlimit(10**5)

In [3]: from so import *

In [4]: %timeit recur(range(10**4)).show()
10 loops, best of 3: 18.2 ms per loop

In [5]: %timeit recurse1(range(10**4))
1 loops, best of 3: 559 ms per loop

In [6]: %timeit recurse2(range(10**4))
1 loops, best of 3: 1e+03 ms per loop

In [7]: %timeit recurse3(range(10**4))
1 loops, best of 3: 1.02 s per loop

In [8]: %timeit recurse4(range(10**4))
1 loops, best of 3: 596 ms per loop

<强>代码:

class recur:
    # No extra memory is required in this method

    def __init__(self,lis):
        self.lis=lis
        self.len=len(self.lis)
        self.rec(0)

    def show(self):
        return self.lis

    def rec(self,n):
        if n!=self.len:
            self.lis[n]-=1
            self.rec(n+1)

def recurse1(l,lis=None):
    lis=lis if lis is not None else []
    if l:
        lis.append(l[0]-1)
        return recurse1(l[1:],lis)
    else:
        return lis

def recurse2(l):
    return [l[0]-1] + recurse2(l[1:]) if l else []

def recurse3(l):  
    if len(l) == 0:  
        return []  
    else:
        return [l[0] -1] + recurse3(l[1:])

def recurse4(l, x = []):  
    if len(l) == 0:  
        return []  
    else:  
        x.append(l[0] -1)  
    recurse4(l[1:], x)  
    return x  

答案 6 :(得分:1)

这是一个递归解决方案,可以处理大型列表而不会达到递归深度限制。通过使用除法和征服,与具有朴素递归的O(N)相比,递归深度最差为O(log(N))。然而,任何类型的递归对于这个问题都是一个很差的技术选择,因为它通过一个简单的for循环来解决。

def dec_list(xs, a, b):
    if b == a + 1:
        xs[a] -= 1
    if a + 1 >= b: return
    mid = (a + b) // 2
    dec_list(xs, a, mid)
    dec_list(xs, mid, b)

def dec(xs):
    dec_list(xs, 0, len(xs))

xs = range(1001)
dec(xs)
print xs