Pythonの最大再帰深度はどのくらいですか、それを増やす方法は?


421

私はこの末尾再帰関数をここに持っています:

def recursive_function(n, sum):
    if n < 1:
        return sum
    else:
        return recursive_function(n-1, sum+n)

c = 998
print(recursive_function(c, 0))

それはまで機能しn=997、その後、壊れて吐き出しRecursionError: maximum recursion depth exceeded in comparisonます。これは単なるスタックオーバーフローですか?それを回避する方法はありますか?



9
メモ化は、スタックサイズを増やす代わりに、以前に計算された値を終了させることにより、関数を高速化し、その効果的な再帰深度を増やすことができます。
Cyoce 2016年

2
再帰制限は通常1000です
ボリス

1
@tonixインタープリターはスタックフレーム(line <n>, in <module>スタック内トレース)を追加し、このコードは2つのスタックフレームを使用しますn=1(ベースケースがn < 1であるため、n=1再帰します)。そして、「1000に達したときのエラー」ではなく「1000(1001)を超えた場合のエラー」のように、再帰制限は包括的ではないと思います。997 + 21000未満なので998 + 2、制限に達したため、機能しません。
ボリス

1
@tonixいいえ。recursive_function(997)動作し、それはで壊れ998ます。呼び出すrecursive_function(998)と、999スタックフレームが使用され、インタープリターによって1フレームが追加されます(コードは常に最上位モジュールの一部であるかのように実行されるため)。これにより、1000の制限に達します。
ボリス

回答:


469

はい、スタックオーバーフローに対するガードです。Python(またはCPython実装)は末尾再帰を最適化せず、制限のない再帰はスタックオーバーフローを引き起こします。で再帰制限を確認し、で再帰制限をsys.getrecursionlimit変更できますがsys.setrecursionlimit、そうすることは危険です。標準の制限は少し控えめですが、Pythonスタックフレームは非常に大きくなる可能性があります。

Pythonは関数型言語ではなく、末尾再帰は特に効率的な手法ではありません。可能であれば、アルゴリズムを反復的に書き換えることは、通常、より良いアイデアです。


4
私の経験から、モジュールsysresourceモジュールの両方で制限を増やす必要があります
。stackoverflow.com/ a

3
それを反復バージョンに変換する戦術として、末尾呼び出し最適化デコレーターを使用できます
jfs

3
svn.python.org/projects/python/trunk/Tools/scripts/…を使用して、OSの上限を確認できます
Ullullu

8
ソースに関心がある場合、デフォルトの再帰制限は1000 hg.python.org/cpython/file/tip/Python/ceval.c#l691に設定されており、hg.python.org / cpythonのAPIを使用して変更できます。/file/tip/Python/sysmodule.c#l643順次に新しい値に制限を設定しhg.python.org/cpython/file/tip/Python/ceval.c#l703
Pramodさん

17
末尾再帰は、最適化されたプログラミング言語で完全に効率的な手法です。適切な種類の問題については、反復的な実装の方が表現力がかなり高くなる可能性があります。答えはおそらく「具体的にはPythonで」を意味しますが、それはそれが言っていることではありません
Peter R

135

より高い再帰深度設定する必要があるようです:

import sys
sys.setrecursionlimit(1500)

私の場合、ベースケースのreturnステートメントを忘れて、それが1000を超えました。Pythonがこの例外をスローし始め、私は驚きました。それを実行するために作成するスタックのスタック。
vijayraj34

sys.setrecursionlimit(50)または少量は、プログラムが再帰を開始していて、エラーメッセージをページや同じテキストのページにしたくない場合に役立ちます。(私の)悪い再帰的なコードをデバッグしているときに、これはとても役に立ちました。
peawormsworth

56

スタックオーバーフローを回避するためです。Pythonインタプリタは、再帰の深さを制限して、無限再帰を回避し、スタックオーバーフローを発生させないようにします。再帰制限(sys.setrecursionlimit)を増やすか、再帰なしでコードを書き直してください。

Pythonのドキュメントから:

sys.getrecursionlimit()

再帰制限の現在の値、Pythonインタープリタースタックの最大深度を返します。この制限により、無限再帰がCスタックのオーバーフローを引き起こしてPythonをクラッシュさせるのを防ぎます。で設定できますsetrecursionlimit()


私のアナコンダのx64、Windows上のPython 3.5では、デフォルトの制限値は1000です
ギヨームシュヴァリエ

30

再帰制限を頻繁に変更する必要がある場合(たとえば、プログラミングパズルを解くとき)、次のような単純なコンテキストマネージャを定義できます。

import sys

class recursionlimit:
    def __init__(self, limit):
        self.limit = limit
        self.old_limit = sys.getrecursionlimit()

    def __enter__(self):
        sys.setrecursionlimit(self.limit)

    def __exit__(self, type, value, tb):
        sys.setrecursionlimit(self.old_limit)

次に、カスタム制限付きの関数を呼び出すには、次のようにします。

with recursionlimit(1500):
    print(fib(1000, 0))

withステートメントの本文を終了すると、再帰制限はデフォルト値に戻ります。


また、を使用してプロセスの再帰制限を引き上げるresourceこともできます。これがないと、セグメンテーションフォールトが発生し、setrecursionlimit高すぎて新しい制限を使用しようとすると、Pythonプロセス全体がクラッシュします(約8メガバイトのスタックフレーム。これは、上記の簡単な関数で約30,000スタックフレームに変換されます)。私のノートパソコン)。
ボリス

16

末尾呼び出しの最適化を保証する言語を使用します。または反復を使用します。または、デコレータでかわいくなります。


36
それはむしろ赤ちゃんを風呂水で捨てるということです。
Russell Borogove、2010

3
@ラッセル:私が提供したオプションの1つだけがこれを助言します。
Marcelo Cantos、2010

「デコレータを使ってかわいく」はオプションではありません。
B氏

Mr.B @あなたがより多く必要としない限り、ulimit -sスタックフレームの、はい、それはあるstackoverflow.com/a/50120316
ボリス

14

resource.setrlimit スタックサイズを増やし、segfaultを防ぐためにも使用する必要があります

Linuxカーネルはプロセスのスタックを制限します

Pythonはローカル変数をインタープリターのスタックに格納するため、再帰はインタープリターのスタックスペースを占有します。

Pythonインタープリターがスタック制限を超えようとすると、Linuxカーネルはそれをセグメンテーション違反にします。

スタック制限サイズはgetrlimitおよびsetrlimitシステムコールで制御されます。

Pythonでは、resourceモジュールを介してこれらのシステムコールにアクセスできます。

import resource
import sys

print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print

# Will segfault without this line.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)

def f(i):
    print i
    sys.stdout.flush()
    f(i + 1)
f(0)

もちろん、ulimitを増やし続けると、RAMが不足し、スワップマッドネスのためにコンピューターの速度が低下するか、OOMキラーを介してPythonが強制終了されます。

bashから、スタック制限(KB単位)を表示して設定できます。

ulimit -s
ulimit -s 10000

私のデフォルト値は8Mbです。

以下も参照してください。

Ubuntu 16.10、Python 2.7.12でテスト済み。


1
スタッククラッシュの修正rlimit_stack後に設定しようとすると、失敗または関連する問題が発生する可能性があります。Red Hat Issue 1463241
jww

これ(Pythonリソース部分)を使用して、コサラジュのアルゴリズムをTim Roughgarden教授の平均(巨大な)データセットに実装しました。私の実装は小さなセットで機能しましたが、確かに大きなデータセットの問題は再帰/スタックの制限でした...それともそうでしたか?ええ、そうでした!ありがとう!
nilo

9

私はこれが古い質問であることを理解していますが、それらを読んでいる人には、このような問題に再帰を使用しないことをお勧めします-リストははるかに速く、再帰を完全に回避します。私はこれを次のように実装します:

def fibonacci(n):
    f = [0,1,1]
    for i in xrange(3,n):
        f.append(f[i-1] + f[i-2])
    return 'The %.0fth fibonacci number is: %.0f' % (n,f[-1])

(フィボナッチ数列を1ではなく0からカウントする場合は、xrangeでn + 1を使用します。)


13
O(1)を使用できるのに、なぜO(n)スペースを使用するのですか?
Janus Troelsen 2014年

11
O(n)スペースのコメントがわかりにくい場合に備えて、リストを使用しないでください。必要なものがn番目の値である場合、リストはすべての値を保持します。単純なアルゴリズムは、最後の2つのフィボナッチ数を保持し、必要な数になるまでそれらを追加することです。より良いアルゴリズムもあります。
ミリメトリック2014

3
@Mathime:xrange単に呼ばれるrange3 Pythonで、
エリック・O Lebigot

1
@EOL私はこれを知っています
Mathime

7
@Mathime私はこれらのコメントを読んでいる人のために物事を明確にしていた。
Eric O Lebigot

9

もちろん、フィボナッチ数はBinet式を適用することでO(n)で計算できます。

from math import floor, sqrt

def fib(n):                                                     
    return int(floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))+0.5))

コメンターが指摘するように、それはO(1)ではなくO(n)です2**n。また、1つの値しか取得できないのに対し、再帰ではFibonacci(n)その値までのすべての値を取得できるという違いもあります。


8
Pythonではlongの最大サイズはありません。
pppery 2015年

8
これは、大規模で失敗したことは注目に値することのnためのポイントの不正確さをフローティング-の違い(1+sqrt(5))**n(1+sqrt(5))**(n+1)なり、1つの未満ULP、あなたは間違った結果を取得を開始ようにします。

2
NumPyには実際には大きな整数はありません…
Eric O Lebigot

@Megoなに?それは違います(1+sqrt(5))**nし、((1+sqrt(5))**n)+1それが1つの未満ULPになります!(小さなタイプミス)また、{@} rwstそれはO(1)ではありません!計算に2**nは少なくともO(n)時間かかります。
user202729 2018年

3
@ user202729それは真実で2**nはありません。計算は、SquaringによるExponentiattionを使用して事実上O(log(n))になります。
サム

6

「最大再帰深度を超えました」というエラーで同様の問題が発生しました。このエラーは、でループしていたディレクトリ内の破損したファイルが原因であることがわかりましたos.walk。この問題の解決に問題があり、ファイルパスを操作している場合は、ファイルが破損している可能性があるため、必ず絞り込んでください。


2
OPは彼のコードを提供し、彼の実験は自由に再現可能です。破損したファイルは含まれません。
T. Verron、2015年

5
あなたは正しいですが、これは4年以上前だったので、私の答えはOPに向けられていません。私の回答は、破損したファイルによって間接的に引き起こされるMRDエラーのあるユーザーを支援することを目的としています-これは最初の検索結果の1つです。それは賛成票を投じられたので、それは誰かを助けました。反対票をありがとう。
タイラー

2
これは、「最大再帰深度」トレースバックを破損したファイルに接続する問題を検索しているときに、私が見つけた唯一のものでした。ありがとう!
ジェフ

5

フィボナッチ数を少しだけ取得したい場合は、行列法を使用できます。

from numpy import matrix

def fib(n):
    return (matrix('0 1; 1 1', dtype='object') ** n).item(1)

numpyは高速指数アルゴリズムを使用しているため、高速です。答えはO(log n)で得られます。また、整数のみを使用するため、Binetの式よりも優れています。ただし、nまでのすべてのフィボナッチ数列が必要な場合は、暗記することをお勧めします。


悲しいことに、ほとんどの競争力のあるプログラミング審査員でnumpyを使用することはできません。しかし、はい、あなたの解決策は私のお気に入りです。私はいくつかの問題にマトリックスソリューションを使用しました。これは、非常に大きなフィボナッチ数が必要で、係数を使用できない場合に最適なソリューションです。モジュラスの使用が許可されている場合は、ピサノピリオドを使用することをお勧めします。
mentatkgs

4

ジェネレーターを使用しますか?

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fibs = fib() #seems to be the only way to get the following line to work is to
             #assign the infinite generator to a variable

f = [fibs.next() for x in xrange(1001)]

for num in f:
        print num

上記のfib()関数からの改作:http : //intermediatepythonista.com/python-generators


1
ジェネレータを変数に割り当てる必要がある理由[fibs().next() for ...]は、毎回新しいジェネレータを作成するためです。
tox123

3

@alexが提案したように、これを再帰的ではなく連続的に行うためにジェネレーター関数を使用できます。

これがあなたの質問のコードと同等のものです:

def fib(n):
    def fibseq(n):
        """ Iteratively return the first n Fibonacci numbers, starting from 0. """
        a, b = 0, 1
        for _ in xrange(n):
            yield a
            a, b = b, a + b

    return sum(v for v in fibseq(n))

print format(fib(100000), ',d')  # -> no recursion depth error

2

多くの場合、再帰制限を増やすことは良い解決策であると推奨されていますが、常に制限があるためではありません。代わりに、反復解を使用してください。

def fib(n):
    a,b = 1,1
    for i in range(n-1):
        a,b = b,a+b
    return a
print fib(5)

1

メモ化を使用してフィボナッチを計算する例を紹介したいと思います。これにより、再帰を使用して非常に大きな数を計算できるようになります。

cache = {}
def fib_dp(n):
    if n in cache:
        return cache[n]
    if n == 0: return 0
    elif n == 1: return 1
    else:
        value = fib_dp(n-1) + fib_dp(n-2)
    cache[n] = value
    return value

print(fib_dp(998))

これはまだ再帰的ですが、以前に計算されたフィボナッチ数を再度実行する代わりに再利用できるようにする単純なハッシュテーブルを使用します。


1
import sys
sys.setrecursionlimit(1500)

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))

1
これと同じ答えが何度も出されています。削除してください。
ZF007

0

これは、@lru_cacheデコレータとsetrecursionlimit()メソッドを使用して行うことができます。

import sys
from functools import lru_cache

sys.setrecursionlimit(15000)


@lru_cache(128)
def fib(n: int) -> int:
    if n == 0:
        return 0
    if n == 1:
        return 1

    return fib(n - 2) + fib(n - 1)


print(fib(14000))

出力

300246876117846109099549417971502564869274793749079294346837542950223024294228483586340233357521621786581163873038935223918134230775672041461939121779854257599654108106050190530215701900261496471731080880947867560271144036124150073269914583437785632639403707166627432165730532080405530702101979325176283081670158738699488803236223219821984354986527588069961235927512524345713249677285488650870339664336504245433300980200638428685958164929639080300323265489846456158923444513986324260628571159174622288080739105721191265581849979872098730254071206795984080210684977654752224742990461835739477172565325355934619528260128501916936020735517922381485710640528500799754769254637875706299958165786718842099577065056552137787433308596312344425895305275146120697761507951143586287967843908117553626557697710686507409951289723510053824119644581556829137784665635297922809891156667595652564418264560817860383717222783889672542560571994230003765052623148688106603739786694201383829676928474552777843927299506723149206936913028915475313231388329439859350787355566721100542200320415615485903152946215295311995759719573595368679887113114825505014045084503424009530509444991157859853965885570415824022180952801017941449349958347356887325306792163951399659673827581790962485759369329198084130329114561356646657523328365142013491576496137287593382226295342044454834918043658318329194487559947724081477458018714463796548725057813499040244336567798538848196149244498199452303424561978185336547655271946096079592966688366570429389731020127601165807435919418935966079249602747222642857154797160225980869744143535857848058983776691168420027563688919225476267851259700045267619137447593279666384286574465826492491377167641540417992009607475151642287299766542504745742832727623005929613272278791530010500201900629332008295537871590826365337775503115579406345051573100940240758468313287020637699402592079029859114421365994266862206219144134620009834294395516952253257427164495436021747245852148967185946523256841940418204396609221174437269979737596604801077545344460015352477223840141478956265141028980899496053313275953209289577940694092525290616661215369985075993376289794717597214786878400832024758621037855671133273946327794025528904796232330694606838188744604638774524792567524018298119083626496464061206990945868244339272994608409931204775296680643933140366393496994295802223794520599258117880360615698203438534718276657335176874966517254990863833761195319980816193788536670928504327659572648406813809118891469815170312277372672526137054235516211816430272881225919247642893873072410982592233197325610509120055156658135050806192276291007852821986991321414657555724919926363424116535222657074961890705055311546830666918448591026980622589453080982310227923175006165204256077253057671314864785870536964964290778060324742868017623652722012658612616650950314064047415639050389812600256576256163509591406427625625665064206425657625616520650140140474762390503905140642561752562562064256576256175206625660014014047656216350377812553656213719016350377914012666256160032065014014047476216350377912553656213719016350377964256756661562563907596642565762561652050600140140474762163503779140124562561190

ソース

functools lru_cache


0

ダイナミックプログラミングボトムアップアプローチのバリエーションを使用することもできます

def fib_bottom_up(n):

    bottom_up = [None] * (n+1)
    bottom_up[0] = 1
    bottom_up[1] = 1

    for i in range(2, n+1):
        bottom_up[i] = bottom_up[i-1] + bottom_up[i-2]

    return bottom_up[n]

print(fib_bottom_up(20000))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.