Генератор рекурсивных перестановок, замена элементов списка не работает

Я хочу систематически генерировать перестановки алфавита. Я не могу не использовать python itertools.permutation, потому что предварительное создание списка каждой перестановки приводит к сбою моего компьютера (в первый раз, когда я действительно заставил его принудительно завершить работу, это было довольно здорово).

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

Моя идея состоит в том, чтобы начать с самого большого списка (в качестве примера я буду использовать список из 3 элементов), рекурсивно переходить к меньшему списку, пока список не будет состоять из двух элементов. Затем он распечатает список, поменяет местами последние два, снова распечатает список, вернется на один уровень вверх и повторит.

Например, для 123

123 (поменять местами позицию 0 с позицией 0)

    23    --> 123 (swap position 1 with position 1)
    32    --> 132 (swap position 1 with position 2)

213 (поменять местами позицию 0 с позицией 1)

    13    --> 213 (swap position 1 with position 1)
    31    --> 231 (swap position 1 with position 2)

321 (поменять местами позицию 0 на позицию 2)

    21    --> 321 (swap position 1 with position 1)
    12    --> 312 (swap position 1 with position 2)

для четырехбуквенного номера (1234)

1234 (поменять местами позицию 0 на позицию 0)

    234    (swap position 1 with position 1)

           34 --> 1234
           43 --> 1243
    324    (swap position 1 with position 2)
           24 --> 1324
           42 --> 1342
    432    (swap position 1 with position 3)
           32 --> 1432
           23 --> 1423

2134 (поменять местами позицию 0 на позицию 1) 134 (поменять местами позицию 1 на позицию 1) 34 --> 2134 43 --> 2143 314 (поменять местами позицию 1 на позицию 2) 14 --> 2314 41 --> 2341 431 (поменять местами позиция 1 с позицией 3) 31 --> 2431 13 --> 2413

Это код, который у меня сейчас есть для рекурсии, но он причиняет мне много горя, рекурсия не моя сильная сторона. Вот что у меня есть.

def perm(x, y, key):
    print "Perm called: X=",x,", Y=",y,", key=",key
    while (x<y):

        print "\tLooping Inward"

        print "\t", x," ",y," ", key
        x=x+1
        key=perm(x, y, key)
        swap(x,y,key)
        print "\tAfter 'swap':",x," ",y," ", key, "\n"

    print "\nFull Depth Reached"
    #print key, " SWAPPED:? ",swap(x,y,key)
    print swap(x, y, key)
    print " X=",x,", Y=",y,", key=",key
    return key

def swap(x, y, key):
    v=key[x]
    key[x]=key[y]
    key[y]=v
    return key

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

Спасибо всем! Комментарии к моему методу или чему-то еще приветствуются.


person Eagle    schedule 09.04.2011    source источник
comment
Во-первых, itertools не создает список заранее, если только вы не ошиблись. Во-вторых, он сломан или вам просто нужен лучший код? Если вы ищете лучший код, вы просто публикуете код для обзора на codereview.stackexchange.com.   -  person Winston Ewert    schedule 09.04.2011
comment
поправлю и выложу туда, спасибо   -  person Eagle    schedule 09.04.2011
comment
Второй Уинстон Эверт; itertools.permutations не создает заранее все перестановки, и, насколько я могу судить, это именно то, что вам нужно. Возможно, вы пытались сделать что-то вроде печати всех его результатов или вставить их в список, из-за чего ему пришлось генерировать их все, чтобы удовлетворить запрос?   -  person dfan    schedule 09.04.2011
comment
делайте это только в том случае, если это работает. Этот сайт предназначен для улучшения рабочего кода, а не для исправления сломанного кода. Из вашего сообщения мне не ясно, сломан ли код. Если да, то вам нужно указать на этом сайте, чем именно он отличается от того, что вы ожидаете.   -  person Winston Ewert    schedule 09.04.2011


Ответы (2)


Случилось на моем старом вопросе позже в моей карьере

Чтобы сделать это эффективно, вам нужно написать генератор.

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

Преимущества генераторов:

  • Take up much less space.
    • Generators take up between 40 and 80 bytes of space. One generators can have generate millions of items.
    • Список с одним элементом занимает 40 байт. Список из 1000 элементов занимает 4560 байт.
  • More efficient
    • Only computes as many values as you need. In permuting the alphabet, if the correct permutation was found before the end of the list, the time spend generating all of the other permutations was wasted.

(Itertools.permutation является примером генератора)

Как написать генератор?

Написать генератор на питоне на самом деле очень просто.
По сути, напишите код, который будет работать для создания списка перестановок. Теперь вместо resultList+=[ resultItem ] напишите yield(resultItem).

Вот вы и сделали генератор. Если бы я хотел зациклить свой генератор, я мог бы написать

for i in myGenerator:

Это так просто.


Ниже представлен генератор кода, который я давно пытался написать:

def permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

person Eagle    schedule 20.06.2012

Я думаю, у вас есть действительно хорошая идея, но отслеживание позиций может оказаться немного сложным. Общий способ, который я видел для рекурсивного создания перестановок, - это функция, которая принимает два строковых аргумента: один для удаления символов из (str) и один для добавления символов в (soFar).

При создании перестановки мы можем подумать о том, чтобы взять символы из str и добавить их в soFar. Предположим, у нас есть функция perm, которая принимает эти два аргумента и находит все перестановки str. Затем мы можем рассмотреть текущую строку str. У нас будут перестановки, начинающиеся с каждого символа в str, поэтому нам просто нужно перебрать str, используя каждый из этих символов в качестве начального символа и вызывая perm для символов, оставшихся в строке:

// half python half pseudocode    
def perm(str, soFar):
    if(str == ""):
        print soFar // here we have a valid permutation
        return

    for i = 0 to str.length:
        next = soFar + str[i]
        remaining = str.substr(0, i) + str.substr(i+1)
        perm(remaining, next)
person Eric Conner    schedule 09.04.2011