【问题标题】:Iteration vs recursion when dynamic lookup table is required需要动态查找表时的迭代与递归
【发布时间】:2014-02-01 23:39:39
【问题描述】:

我想知道在使用动态查找表时是否可以使用迭代。使用递归,您可以从下到上获取所有必要的查找表数据。我似乎无法理解我们如何使用迭代来实现相同的目标,而无需创建堆栈并最终重新实现递归。

以我对 Project Euler 问题 #14 的解决方案为例:

table = {1:1}

def collatz(n):
    if n in table:
        return table[n]
    elif n % 2:
        x = 1 + collatz(3*n + 1)
    else:
        x = 1 + collatz(n/2)
    table[n] = x
    return x

MAXc = 0

for i in xrange(1,1000000):
    temp = collatz(i)
    if temp > MAXc:
        MAXc = temp
        result = i

print result

我们如何使用迭代来实现相同的功能?

【问题讨论】:

    标签: python recursion iteration theory


    【解决方案1】:

    只计算步数直到达到 1 的迭代算法是微不足道的。问题是还要更新所有中间值的缓存。

    在这种特殊情况下,有一个不需要显式堆栈的迭代算法。它使用两次:第一次计算总步数,第二次更新缓存。

    def next(n):
        if n % 2 != 0:
            return 3*n + 1
        else:
            return n/2
    
    def collatz(n):
        count = 0
        i = n
        while i not in table:
            count += 1
            i = next(i)
        count += table[i]
        i = n
        while i not in table:
            table[i] = count
            count -= 1
            i = next(i)
        return table[n]
    

    【讨论】:

    • 这正是我想要的!我想知道性能是否有明显差异。除非迭代版本快得多,否则我认为自己不会花任何时间进行迭代实现,考虑到递归版本更直接明了。
    • 迭代版本在我的机器上实际上要慢 x1.5。删除第二遍(并且仅更新 table[n] 的缓存)使其速度与递归版本大致相同。
    【解决方案2】:

    诸如(动态构建代码,对于任何错别字/错误,非常抱歉):

    def collatz_generator(n):
        while n != 1:
            n = n & 1 and (3 * n + 1) or n / 2
            yield n
    
    def add_sequence_to_table(n, table):
        table = table or {1:1}
        sequence = list(collatz_generator(n))
        reversed = list(enumerate(sequence))[::1]
    
        for len, num in reversed:
            if num in table:
                break
            table[n] = len + 1
        return table
    
    def build_table(n):
        table = add_sequence_to_table(2)
        for n in xrange(3, n):
            table = add_sequence_to_table(n, table)
    
        return table
    

    没有构建表(因为妻子要我阅读而即时打字):

    def without_table(n):
        max_l, examined_numbers = 0, set()
        for x in xrange(2, n):
            reversed = list(enumerated(collatz_generator(x)))[::-1]
            for num, length in reversed:
                if num in examined_numbers:
                    break
    
                examined_numbers.add(num)
    
                if num <= n:  # I think this was a problem requirement.
                    max_l = max(max_l, length)
    

    这不行吗?

    【讨论】:

    • 这会生成所有的数字,并将它们保存在一个列表中,相当于使用堆栈。
    • 是的。我试图从我的第一个答案中得到我的递归,这是你想要的,但正如你所指出的那样,这不是重点。
    • 我把桌子放下了。我确实使用了一组来跟踪检查的数字。
    • @tom 我被困在一个数组中(reversed),但它也可以分解成一个生成器。更多关于你的理论问题。聪明的生成器不能完成你想要的吗?
    • 堆栈与迭代深度搜索算法中使用的堆栈相同。我不是在谈论查找表,我当然不排除所有堆栈。只是那些最终类似于编译器为递归创建的那些。
    猜你喜欢
    • 2018-04-23
    • 2012-04-26
    • 2018-05-04
    • 2011-11-09
    • 2018-08-16
    • 2012-08-29
    • 2021-05-26
    • 1970-01-01
    • 2015-07-18
    相关资源
    最近更新 更多