Глубина рекурсии Python превысила лимит, и не знаю, как удалить рекурсию - PullRequest
0 голосов
/ 11 декабря 2011

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

import random

def main():
    global tries,moves
    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y
    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]
    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

def start():
    global i,indexes,moves,tries
    i=0
    random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
    while i<=7:
        if indexes[i]==0:
            move(-2,-1)
        elif indexes[i]==1:
            move(-2,1)
        elif indexes[i]==2:
            move(-1,-2)
        elif indexes[i]==3:
            move(-1,2)
        elif indexes[i]==4:
            move(1,-2)
        elif indexes[i]==5:
            move(1,2)
        elif indexes[i]==6:
            move(2,-1)
        elif indexes[i]==7:
            move(2,1)
        i+=1
    for _ in a:
        if 0 in _:
            print "Wasted moves: %d"%(moves)
            tries+=1
            moves=0
            restart()
    print "Success obtained in %d tries"%(tries)

def move(column,row):
    global x,y,a,moves
    try: b=a[x+row][y+column]
    except IndexError: return 0
    if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x=x+row
        y=y+column
        a[x][y]=1
        moves+=1
        start()
    else: return 0

try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors

РЕДАКТИРОВАТЬ: Это модифицированная версия, которая по-прежнему не работает, но по крайней мере это улучшение:

import random

def move((column,row),x,y):
    try: cell=squares_visited[x+row][y+column]
    except IndexError: return x,y ## NONE TYPE OBJECT
    if cell==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x+=row
        y+=column
        squares_visited[x][y]=1
        return x,y ## NONE TYPE OBJECT

squares_visited=[[0] * 8 for _ in range(8)]
x=random.randint(0,7)
y=random.randint(0,7)
squares_visited[x][y]=1
moves=[(-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)]
indexes=list(range(8))
tries=0
print "The horse starts in position %d,%d"%(x,y)

while True:
    random.shuffle(indexes)
    for _ in indexes:
        cells=move(moves[indexes[_]],x,y) ##Passing as arguments x and y looks weird
        x=cells[0]
        y=cells[1]
    #If you out this for cicle, there are no legal moves anymore(due to full completion with 1, or to lack of legit moves)
    for _ in squares_visited:
        if 0 in _:
            squares_visited=[[0] * 8 for _ in range(8)]
            tries+=1
    else:
        print "You managed to do it in %d tries."%(tries)

Ответы [ 2 ]

8 голосов
/ 11 декабря 2011

В этом коде много проблем - достаточно того, что его стоит полностью изучить:

import random

def main():
    global tries,moves

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

    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y

Почему вы называете свою доску a?Это ужасное имя!Используйте что-то описательное, например squares_visited.

    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]

Minor: в python 2 [x for x in range(8)] == range(8) - они делают одно и то же, поэтому понимание списка не требуется.В 3 он работает немного по-другому, но если вам нужен список (а не range объект), просто передайте его list, как в (list(range(8))).

    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

Итак, мое понимание кода на данный момент таково, что a - это доска, x и y - начальные координаты, и вы отметили первое посещенное место с помощью 1.Все идет нормально.Но потом все начинает становиться неопрятным, потому что вы вызываете start в конце restart вместо того, чтобы вызывать его из функции управления верхнего уровня.Это теоретически нормально, но делает рекурсию более сложной, чем необходимо;это еще одна часть вашей проблемы.

def start():
    global i,indexes,moves,tries

Ага, еще больше глобалов ...

    i=0
    random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
    while i<=7:
        if indexes[i]==0:
            move(-2,-1)
        elif indexes[i]==1:
            move(-2,1)
        elif indexes[i]==2:
            move(-1,-2)
        elif indexes[i]==3:
            move(-1,2)
        elif indexes[i]==4:
            move(1,-2)
        elif indexes[i]==5:
            move(1,2)
        elif indexes[i]==6:
            move(2,-1)
        elif indexes[i]==7:
            move(2,1)
        i+=1

Хорошо, так что вы пытаетесь последовательно просмотреть каждый индекс в indexes.Почему вы используете while?И почему i глобальный ??Я не вижу его где-либо еще.Это слишком сложно.Просто используйте цикл for для итерации по indexes напрямую, как в

    for index in indexes: 
        if index==0:
            ...

Хорошо, теперь для конкретных проблем ...

    for _ in a:
        if 0 in _:
            print "Wasted moves: %d"%(moves)
            tries+=1
            moves=0
            restart()
    print "Success obtained in %d tries"%(tries)

Я не понимаючто вы пытаетесь сделать здесьКажется, что вы звоните restart каждый раз, когда вы обнаруживаете 0 (то есть не посещенное место) на вашей доске.Но restart сбрасывает все значения платы до 0, поэтому, если нет другого способа заполнить доску 1 с до достижения этой точки, это приведет к бесконечной рекурсии.Фактически, взаимная рекурсия между move и start может быть в состоянии достичь этого в принципе, но на самом деле это слишком сложно!Проблема в том, что нет четкого условия завершения рекурсии.

def move(column,row):
    global x,y,a,moves
    try: b=a[x+row][y+column]
    except IndexError: return 0
    if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x=x+row
        y=y+column
        a[x][y]=1
        moves+=1
        start()
    else: return 0

Здесь, в принципе, кажется, что если ваш ход достигнет 1 или уйдет с доски, то текущая ветвь рекурсии заканчивается.Но поскольку i и indexes глобальны выше в start, когда start вызывается повторно, он перетасовывает indexes и сбрасывает i в 0!Результат - хаос!Я даже не могу понять, как это повлияет на рекурсию;вполне вероятно, что поскольку i каждый раз сбрасывается в начале start, а каждый успешный вызов move приводит к вызову start, цикл while при запуске не будет никогда прекратить!

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

try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors

Не уверен, что вы подразумеваете под этой последней строкой, но это не похоже на хороший знак - вы исправляете ошибку, а не находите основную причину.

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

Обновление :

Хорошо, я глобализировал indexes, как описано выше.Затем я заменил рекурсию start / restart на бесконечный цикл в restart, оператор return в start, где раньше был вызов restart, и sys.exit() в концеstart (чтобы выйти из бесконечного цикла при успехе).Результат ведет себя больше, чем ожидалось.Это все еще плохой дизайн, но он работает сейчас, в том смысле, что он рекурсивно пробует несколько случайных путей, пока не будет посещена каждая локальная позиция.

Конечно, это все еще не удавалось;это просто продолжает цикл.На самом деле поиск решения, вероятно, потребует гораздо большего переосмысления этого алгоритма!Но, следуя моим вышеизложенным предложениям, некоторые должны помочь, по крайней мере.

3 голосов
/ 11 декабря 2011

start() и move() вызывают друг друга, делая это косвенным рекурсивным вызовом, НО move() return стартовый выход из move() и не из рекурсии.

Вы видите,когда вы вызываете функцию, которая вызывает функцию, которая вызывает функцию, все это помещается в стек, который ссылается на каждый вызов.Когда вы получите ваш окончательный результат, вы должны вернуться назад и разложить эти вызовы функций.

Здесь вы не делаете, вы вызываете move(), который вызывает start(), и возвращаетзатем вы просто продолжаете углубляться в стек.

Попробуйте создать итеративную версию своего кода.Рекурсия трудна, начните с чего-то более легкого.

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

Кстати:

  • Избегайте использования глобальных переменных.Либо передайте данные в качестве аргументов, либо используйте класс.Я бы использовал передачу аргументов, это заставит вас придумать лучший алгоритм, чем этот.
  • цикл while не нужен.Замените его на цикл for для индексов
  • огромный оператор if/elif не нужен, замените его словарем

В результате вы должны получить что-то вроде этого:

for i in indexes:
    move(*MOVES[i])

MOVES, являющийся набором значений i, связанных с параметрами для move().

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

[x for x in range(8)] можно записать range(8) [[0 for y in range(8)] for x in range(8)] можно записать [[0] * 8 for x in range(8)]

range() можно заменить на xrange()

...