【问题标题】:Implementing an Iterative Process: Python实现迭代过程:Python
【发布时间】:2016-11-15 17:11:30
【问题描述】:

我用这个代码找到一个数字的最小除数:

def smallestDisvisor(n):
    return factor(n,2)

def factor(n,divisor):
    if (square(divisor) - n > 0):
        return n
    elif (n % divisor == 0):
        return divisor
    else:
        return factor(n,divisor + 1)

print(smallestDisvisor(32452843)) 

但是,当我以足够大的值运行它时,我得到:

RecursionError: maximum recursion depth exceeded
[Finished in 0.5s with exit code 1]

我不明白递归错误。这段代码不是实现了一个迭代过程吗?

factor(32452843,2) -> factor(32452843,3) -> factor(32452843,4)...

如果没有,我该如何实现这个算法的迭代过程?

【问题讨论】:

  • 你必须使用 while 循环而不是递归
  • Python 没有尾调用优化 (see this question),因此递归不会像您描述的那样展开。您需要使用循环。
  • 你是说我们不能在 Python 中实现迭代过程?
  • 实现迭代过程非常简单——但这建议您使用迭代控制结构:for、while 等。
  • 好吧...实际上递归过程和递归过程调用是不同的:见stackoverflow.com/questions/17254240/…

标签: python recursion


【解决方案1】:

正如 cmets 中所述,Python 没有尾递归优化。您看到错误只是因为堆栈变得太大而无法处理。

编辑

这是迭代获得最小因子的一种方法:

# if you're using python3, import reduce from functools
from functools import reduce

def get_factors(n):
    factors = []
    i = 2
    while i < n:
        if (n % i) == 0:
            factors.append(i)
            if reduce(lambda x,y: x*y, factors) >= n:
                return factors
        i = i + 1
    return factors

这将返回所有因素的列表。要获得最小的,您可以查看第一个元素。由于 32452843 是素数,因此列表将为空。

【讨论】:

    【解决方案2】:

    现在我明白了问题所在:使用递归实现迭代过程。

    是的,您可以这样做。让你绊倒的是默认的堆栈递归限制。由于您对素数 N 的递归需要 sqrt(N) 次调用,因此您只能处理

    您的选择是从默认的 1000 增加深度:

    import sys
    sys.setrecursionlimit(10000)
    

    或者使用一种调用效率更高的算法,例如只考虑素数除数的算法:

    def smallestDisvisor(n):
        return factor(n, 2, [])
    
    def factor(n, divisor, prime_list):
        # See whether any prime divides teh current divisor.
        #   Increment it until we get a new prime.
        while any(divisor % prime == 0 for prime in prime_list):
            divisor += 1 
    
        if (divisor * divisor > n):
            return n
        elif (n % divisor == 0):
            return divisor
        else:
            return factor(n, divisor + 1, prime_list + [divisor] )
    
    print(smallestDisvisor(32452843)) 
    

    回复评论:

    一个调用将一个帧添加到堆栈中。无论过程的理论性质如何,您都无法在不添加堆栈帧的情况下进行函数调用。这是运行时系统固有的。如果您希望迭代过程只有一个堆栈帧,那么您必须编写一个迭代实现。

    【讨论】:

    • 但迭代过程不应该只使用一个堆栈帧。
    • @sarthak 是的,如果它使用 while/for 循环构造作为迭代实现,但在这里您使用的是递归调用,即使您使用尾递归,python 也不知道如何优化它形式,因此每次调用都会为您调用的每个函数添加一个新的堆栈框架,而不管函数是什么。
    猜你喜欢
    • 1970-01-01
    • 2013-11-02
    • 2023-04-09
    • 2015-08-13
    • 1970-01-01
    • 2020-07-22
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多