当前位置: 首页 > 知识库问答 >
问题:

为什么Python递归如此昂贵,我们能对此做些什么呢?

屠锐
2023-03-14
#include <iostream>
using namespace std;

pair<int, int> fib(int n){
    if(n==1) return pair<int, int>(1, 2);
    pair<int, int> x=fib(n-1);
    return pair<int, int>((x.first+x.second) % 997, (x.first+2*x.second) % 997);
}

int main(){
    cout << fib(500).first;
}
def fib(n):
    if n==1:
        return (1, 2)
    x=fib(n-1)
    return ((x[0]+x[1]) % 997, (x[0]+2*x[1]) % 997)

if __name__=='__main__':
    print(fib(500)[0])

两者都将找到答案996没有问题。我们使用modulos来保持合理的输出大小,使用pair来避免指数分支。

对于n=5000,C++代码输出783,但Python会抱怨

RecursionError: maximum recursion depth exceeded in comparison

如果我们加上几行

import sys

def fib(n):
    if n==1:
        return (1, 2)
    x=fib(n-1)
    return ((x[0]+x[1]) % 997, (x[0]+2*x[1]) % 997)

if __name__=='__main__':
    sys.setrecursionlimit(5000)
    print(fib(5000)[0])

共有1个答案

贝滨海
2023-03-14

问题是Python对递归函数调用的数量有一个内部限制。

这个限制是可配置的,如昆汀·库姆斯的答案所示。但是,过深的函数链会导致堆栈溢出。这个底层限制1同时适用于C++和Python。这种限制也适用于所有函数调用,而不仅仅是递归调用。

一般来说:您不应该编写具有线性复杂度或更糟的递归深度增长的2算法。对数增长递归通常很好。尾递归函数作为迭代重写是很简单的。其他递归可以使用外部数据结构(通常是动态堆栈)转换为迭代。

1基础限制是执行堆栈大小。默认大小在不同的系统中是不同的,不同的函数调用消耗不同的内存,因此限制不是以调用次数来指定的,而是以字节来指定的。这在某些系统上也是可配置的。由于可移植性方面的考虑,我通常不建议触及该限制。

2这个经验法则的例外是某些保证尾部递归消除的函数语言(如Haskell),在保证消除的递归的情况下,该规则可以放松。Python不是这样一种语言,所讨论的函数也不是尾递归的。虽然C++编译器可以将消除作为一种优化来执行,但这并不保证,而且通常在调试构建中没有进行优化。因此,这个例外通常也不适用于C++。

免责声明:以下是我的假设,实际上我不知道它们的基本原理:Python限制可能是一个检测潜在无限递归的特性,防止可能不安全的堆栈溢出崩溃,并替换一个更受控制的recursionerr

为什么在C++中递归调用要便宜得多?

C++是一种编译语言。对Python进行了解释。在C++中,几乎所有的东西都便宜,除了从源代码到可执行程序的翻译。

 类似资料:
  • 我正在调查为什么我们在宇宙中耗尽了那么多RU。我们的写操作达到了预期的RU数量,但我们的读操作达到了极限——比我们的写操作高出一个数量级。我试着把它剥离到最简单的场景。在没有结果的分区上查询单个请求会使用2000个RU。为什么这么贵? 集合的分区键是partitionKey。RU容量直接在容器上设置,而不是共享。我们有一个与where子句匹配的复合索引-\u ts asc,id asc。虽然我不知

  • 问题内容: 我对Java真的很陌生,我读到Java 非常昂贵。我只想知道什么是昂贵的,它又如何昂贵? 谢谢。 问题答案: 也许还没有你想的那么糟 它曾经是可怕的(这可能就是您读到它“非常昂贵”的原因)。这些模因可能需要很长时间才能消失 由于涉及缓存刷新和失效的规则,因此Java语言中的同步块通常比许多平台提供的关键部分功能更为昂贵,而这些平台通常使用原子的“测试并设置位”机器指令来实现。即使程序仅

  • 由于找不到文件,send方法返回null BufferedReader。Eclipse只是说有一个NullPointerException是因为print方法,但是当我移除所有try/catch语句时,Eclipse说我需要编写方法抛出IOException或FileNotFoundException,它也允许我这样做,如果我不这样做,它就抛出一个FileNotFoundException。然而,

  • 问题内容: 我不太确定这是什么意思或在做什么,有人可以详细说明吗? 问题答案: 它接受发送者引用的对象,并尝试将其转换为Player类型。Java对象是强类型的,这意味着您必须声明对象的类型。 如果发件人引用的对象不能转换为Player对象,则将为InvalidCast抛出异常。

  • 问题内容: 如果html文件是本地文件(在我的C驱动器上),则可以使用,但是如果html文件在服务器上并且图像文件是本地文件,则无法使用。这是为什么? 任何可能的解决方法? 问题答案: 如果客户端可以请求本地文件系统文件,然后使用JavaScript找出其中的内容,则将是一个安全漏洞。 解决此问题的唯一方法是在浏览器中构建扩展。Firefox扩展和IE扩展可以访问本地资源。Chrome的限制更为严

  • 问题内容: 我正在尝试通过查看其代码来复制我进入gmail的邮件程序。我在多个源代码查看器中看到了很多: 3D是否是我不知道的某种邮件渲染方式? 问题答案: 这是一种称为“ quoted-printable ”的电子邮件编码系统,该系统允许将非ASCII字符表示为ASCII以便进行电子邮件传输。 在带引号的可打印格式中,任何非标准的电子邮件八位字节均以一个符号表示,后跟两个十六进制数字,代表该八位