Рекурсия в Python? RuntimeError: превышена максимальная глубина рекурсии при вызове объекта Python

Возможный дубликат:
Максимальная глубина рекурсии?

У меня другая проблема с моим кодом. Я пишу свою первую программу на Vpython, и мне нужно сделать симуляцию смешивания двух газов. Сначала у меня была проблема с границами, но теперь, когда шары (которые представляют частицы газа) остаются в пределах границ, происходит что-то другое. Через несколько секунд я получаю сообщение об ошибке, которое показано под исходным кодом моей функции. Код:

def MovingTheBall(listOfBalls,position,numCell,flagOfExecution):
    flag = 0
    if flagOfExecution==0:
        positionTmp = position
    else:
        positionTmp = (position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0)
    for i in range( 0, len(listOfBalls) ):
        if positionTmp==listOfBalls[i].pos:
            flag=1


    if flag==1:
        return MovingTheBall(lista,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
    else:
        if positionTmp[0]==0 or positionTmp[0]>=numCell or positionTmp[0]<=-numCell or positionTmp[1]>=numCell or positionTmp[1]<=-numCell:
            return MovingTheBall(lista,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)

        return positionTmp

ошибка:

    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 138, in MovingTheBall
    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 138, in MovingTheBall
    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 138, in MovingTheBall
    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 138, in MovingTheBall
    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 138, in MovingTheBall
    return MovingTheBall(listOfBalls,(position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0),numCell,1)
  File "gaz.txt", line 130, in MovingTheBall
    if positionTmp==listOfBalls[i].pos:
RuntimeError: maximum recursion depth exceeded while calling a Python object

Кто-нибудь может придумать способ упростить мою функцию?

Я запускаю функцию в цикле while:

while 1:
        rate(20)
        for i in range(0,len(self.listOfBalls)):
            self.listOfBalls[i].pos=poruszanie(self.listOfBalls,self.listOfBalls[i].pos,self.numCell,0)

person Emil Smęt    schedule 08.01.2013    source источник
comment
Кроме того, возможно, но в целом хорошая практика - иметь английские имена для переменных и функций. Никогда не знаешь, кто может прочитать ваш код - например, пользователи Stackoverflow :-)   -  person Kim Hansson    schedule 08.01.2013
comment
Может помочь, если вы объясните свой мыслительный процесс о том, для чего предназначен этот код. Возможно, будет достаточно английских имен переменных, а может и нет.   -  person jgritty    schedule 08.01.2013
comment
Я отредактировал сообщение, вы можете мне помочь? Как вы, наверное, заметили, английский - не мой родной язык.   -  person Emil Smęt    schedule 08.01.2013
comment
В качестве примечания, ваш for i in range(0,len(listOfBalls)) блок можно переписать как: flag = any(positionTmp==i.pos for i in listOfBalls)   -  person mgilson    schedule 08.01.2013


Ответы (4)


В Python отсутствует оптимизация хвостовой рекурсии, обычная для функциональных языков, таких как lisp. В Python рекурсия ограничена 999 вызовами (см. sys.getrecursionlimit).

Если глубина 999 больше, чем вы ожидаете, проверьте, нет ли в реализации условия, останавливающего рекурсию, или этот тест может быть неправильным в некоторых случаях.

Осмелюсь сказать, что в Python реализации чисто рекурсивного алгоритма некорректны / безопасны. Реализация fib (), ограниченная 999, на самом деле неверна. Всегда можно преобразовать рекурсивный в итерационный, и это тривиально.

Это достигается нечасто, потому что во многих рекурсивных алгоритмах глубина имеет тенденцию быть логарифмической. Если это не относится к вашему алгоритму, и вы ожидаете рекурсии глубже, чем 999 вызовов, у вас есть два варианта:

1) Вы можете изменить лимит рекурсии с помощью sys.setrecursionlimit(n) до максимума, разрешенного для вашей платформы:

sys.setrecursionlimit(limit):

Установите максимальную глубину стека интерпретатора Python для limit. Этот предел предотвращает бесконечную рекурсию от переполнения стека C и сбоя Python.

Максимально возможный предел зависит от платформы. Пользователю может потребоваться установить более высокий предел, если у него есть программа, требующая глубокой рекурсии, и платформа, поддерживающая более высокий предел. Это следует делать с осторожностью, поскольку слишком высокий предел может привести к сбою.

2) Вы можете попробовать преобразовать алгоритм из рекурсивного в итеративный. Если глубина рекурсии больше, чем разрешено вашей платформой, это единственный способ решить проблему. В Интернете есть пошаговые инструкции и это должна быть простая операция для кого-то с некоторым образованием в области CS. Если у вас возникли проблемы, задайте новый вопрос, чтобы мы могли помочь.

person Paulo Scardine    schedule 08.01.2013
comment
Я читал об этом и нашел на этом веб-сайте несколько ошибок с тем же именем, но вопрос не в том, что такое ошибка, а в том, как ее исправить или упростить код. Я также пытался увеличить импорт стека sys sys.setrecursionlimit (10000) - person Emil Smęt; 08.01.2013
comment
Извините, мне трудно следовать вашему коду, и я не могу указать, где он неправильный. Обновленный ответ с советом о том, как решить вашу проблему, либо проверьте, почему вы не останавливаете рекурсию, либо перейдите на нерекурсивный подход (существуют общие методы преобразования рекурсивной реализации в нерекурсивную). - person Paulo Scardine; 08.01.2013
comment
Np. Я изменил рекурсию на итерацию, ответ я опубликую ниже. - person Emil Smęt; 09.01.2013
comment
Я не согласен с тем, что изменение рекурсивных алгоритмов на итерационные алгоритмы тривиально. - person Ely; 09.01.2016
comment
@Elyasin см. blog.moertel.com/posts/2013 -05-11-recursive-to-iterative.html - person Paulo Scardine; 10.01.2016
comment
Спасибо, Пауло. Однако многие примеры носят более академический характер. Я видел рекурсивные функции, которые нетривиально преобразовать в итерационные функции. - person Ely; 10.01.2016
comment
Я думаю, что каждый рекурсивный алгоритм, который является хвостовой рекурсией, тривиально реализовать как итеративный. Если они не являются хвостовыми рекурсивными, они, возможно, не оптимальны даже для функциональных языков. Конечно, я могу ошибаться, поэтому я был бы признателен, если бы попробовал несколько рекурсивных алгоритмов, которые, по вашему мнению, сложно перевести. - person Paulo Scardine; 13.03.2018

Я изменил рекурсию на итерацию.

def MovingTheBall(listOfBalls,position,numCell):
while 1:
    stop=1
    positionTmp = (position[0]+choice([-1,0,1]),position[1]+choice([-1,0,1]),0)
    for i in range(0,len(listOfBalls)):
        if positionTmp==listOfBalls[i].pos:
            stop=0
    if stop==1:
        if (positionTmp[0]==0 or positionTmp[0]>=numCell or positionTmp[0]<=-numCell or positionTmp[1]>=numCell or positionTmp[1]<=-numCell):
            stop=0
        else:
            return positionTmp

Хорошо работает: D

person Emil Smęt    schedule 08.01.2013
comment
Поздравляю, этот раствор более питонический. - person Paulo Scardine; 09.01.2013
comment
у меня такая же ошибка. что вы имеете в виду под итерацией против рекурсии? не могли бы вы объяснить мне, как я могу сделать этот скрипт итеративным, а не рекурсивным? - person oldboy; 11.10.2018

Ошибка - переполнение стека. Это должно быть звонком на этом сайте, не так ли? Это происходит потому, что вызов poruszanie приводит к другому вызову poruszanie, увеличивая глубину рекурсии на 1. Второй вызов приводит к другому вызову той же функции. Это происходит снова и снова, каждый раз увеличивая глубину рекурсии.

Теперь используемые ресурсы программы ограничены. Каждый вызов функции занимает определенное количество места поверх того, что называется стеком. Если достигается максимальная высота стека, вы получаете ошибку переполнения стека.

person pvoosten    schedule 08.01.2013
comment
у меня такая же ошибка, но я не знаю, как сделать этот скрипт итеративным вместо рекурсивного ... какие-нибудь идеи? - person oldboy; 11.10.2018
comment
я думаю, я мог бы сначала получить все ссылки на страницы различий, а затем перебрать их - person oldboy; 11.10.2018
comment
Вы можете использовать хвостовую рекурсию, как описано в этом ответе - person pvoosten; 11.10.2018
comment
никогда не слышал об этом, но я плохо проверил, спасибо! - person oldboy; 12.10.2018
comment
да, по-видимому, python не поддерживает хвостовую рекурсию в соответствии с этим ответом - person oldboy; 12.10.2018

Это ошибка, которую вы получаете, когда функция делает слишком много рекурсивных вызовов самой себе. Это может быть связано с тем, что базовый вариант никогда не выполняется (и, следовательно, он застревает в бесконечном цикле), или просто из-за большого количества обращений к самому себе. Вы можете заменить рекурсивные вызовы циклами while.

person mojzu    schedule 08.01.2013
comment
Вы можете показать код купола? - person Philippe Boissonneault; 09.01.2013