【问题标题】:Unravel recursion inside double for loop to avoid stack overflow解开双循环内的递归以避免堆栈溢出
【发布时间】:2020-04-07 23:31:01
【问题描述】:

我有以下代码。这就像我能做到的一样简单。有谁知道将这种递归变成循环的巧妙方法?

问题是我可能会遇到回避限制。我想了一些方法来重写它,但它们一点也不漂亮。

此时我最好的想法是我可以把它变成某种尾递归形式,但我不知道该怎么做。

def blackbox(c, i): #This is a different function in production
    if i > 5:
        return range(0,1)
    else:
        return range(0,c+i)

def recurse(c, length):
    if length == 0:
        return [[]]
    return [l + [j] for j in blackbox(c, length) for l in recurse(c - j, length - 1)]

示例:recurse(6, 1000) 抛出的错误远远超出递归限制。

很酷,几乎没用的事实:将range(i, c + 1) 用于黑盒会返回所有长度为length 且总和最多为c 的列表。

编辑:我知道我可以记住代码,但这并不能解决递归限制。在这个例子中,记忆对速度有很大帮助,但在我的情况下它没有,所以我不关心它。

编辑 2:更新了 blackbox,因此 recurse(6,1000) 的值是合理的。

【问题讨论】:

    标签: python recursion


    【解决方案1】:

    一种方法是使用您自己的生成器函数堆栈:

    def blackbox(c, i):
        return range(0, c + i) #This code is actually quite different, treat it as a black box
    
    # For testing at the end
    def recurse(c, length):
        if length == 0:
            return [[]]
        return [l + [j] for j in blackbox(c, length) for l in recurse(c - j, length - 1)]
    
    
    
    # Non-recursive variant following:
    
    gen_stack = []
    
    def gen_driver():
        prevResult = None
    
        while gen_stack:
            try:
                if prevResult is not None:
                    gen_stack[-1].send(prevResult)
                    prevResult = None
                else:
                    next(gen_stack[-1])
            except StopIteration as si:
                prevResult = si.value
                del gen_stack[-1]
    
        return prevResult
    
    
    def nonrecurse(c, length):
        if length == 0:
            return [[]]
    
        # Unfortunately the concise list comprehension doesn't work
        result = []
        for j in blackbox(c, length):
            gen_stack.append(nonrecurse(c - j, length - 1))
            for l in (yield):
                result.append(l + [j])
    
        return result
    
    
    gen_stack.append(nonrecurse(6, 10))
    
    # Testing equality of both variants
    print(gen_driver() == recurse(6,10))
    
    # No crash but I didn't wait until it was ready
    gen_stack.append(nonrecurse(6, 1000))
    

    变体略短,但需要更多注意:

    gen_stack = []
    
    def gen_driver():
        prevResult = None
    
        while gen_stack:
            try:
                if prevResult is not None:
                    gen_stack.append(gen_stack[-1].send(prevResult))
                    prevResult = None
                else:
                    gen_stack.append(next(gen_stack[-1]))
            except StopIteration as si:
                prevResult = si.value
                del gen_stack[-1]
    
        return prevResult
    
    
    def single_generator(value):
        return value
        yield # Mark it as generator function
    
    
    def nonrecurse(c, length):
        if length == 0:
            return single_generator([[]])
    
        return [l + [j] for j in blackbox(c, length) for l in (yield nonrecurse(c - j, length - 1))]
    
    
    gen_stack.append(nonrecurse(6, 10))
    
    # Testing equality of both variants
    print(gen_driver() == recurse(6,10))
    

    虽然在第一个变体中 nonrecurse 是一个生成器函数,但它现在是一个返回生成器的常用函数,其中列表推导本身就是一个生成器。

    【讨论】:

    • 我将不得不阅读 python 的生成器函数概念,然后才能准确判断这一点。我注意到它似乎只适用于 python 3。我希望我的代码同时在 python 2 和 3 上运行。这是只适用于 python 3 的类型吗?编辑:错误是SyntaxError: 'return' with argument inside generator。但是,它在 python3 上运行完美。
    • 好的,我让它工作并简化了一点。我将用我的更改编辑您的答案,并将标记为答案。
    猜你喜欢
    • 2015-07-15
    • 2013-06-28
    • 1970-01-01
    • 2011-08-15
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    相关资源
    最近更新 更多