Как я могу получить все возможные перестановки списка с помощью Common Lisp?

Я пытаюсь написать функцию Common Lisp, которая даст мне все возможные перестановки списка, используя каждый элемент только один раз. Например, список '(1 2 3) даст вывод ((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)).

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

Спасибо, Джейсон


person Jason    schedule 18.01.2010    source источник
comment
обычно хорошей идеей будет опубликовать код, который вы уже написали. Таким образом, мы можем видеть, как вы думаете...   -  person Rainer Joswig    schedule 18.01.2010
comment
Если это домашнее задание, пожалуйста, отметьте его как таковое.   -  person Jason Orendorff    schedule 19.01.2010
comment
Это не домашнее задание. Я намеренно опустил код, который у меня есть до сих пор. Я не хочу портить ответы своей ошибочной идеей.   -  person Jason    schedule 19.01.2010


Ответы (3)


В качестве базового подхода «все перестановки» следуют этому рекурсивному шаблону:

  all permutations of a list L is:
    for each element E in L:
      that element prepended to all permutations of [ L with E removed ]

Если мы примем как данность, что в вашем списке нет повторяющихся элементов, должно выполняться следующее:

(defun all-permutations (list)
  (cond ((null list) nil)
        ((null (cdr list)) (list list))
        (t (loop for element in list
             append (mapcar (lambda (l) (cons element l))
                            (all-permutations (remove element list)))))))
person Vatine    schedule 18.01.2010
comment
Благодарю вас! Это похоже на то, что было у меня, но с некоторыми небольшими и важными отличиями. Единственная проблема, которую я вижу в этом, заключается в том, что (все перестановки '(1 2 3)) и (все перестановки '(1 1 2 3)) дают один и тот же результат, но это должно быть достаточно легко изменить. (Моя конечная цель здесь — перепутать слова.) - person Jason; 19.01.2010
comment
Если у вас есть неразличимые элементы, это становится немного сложнее, вам нужно будет выполнить некоторую предварительную обработку, чтобы избежать создания одной и той же перестановки более одного раза. Однако вместо использования списка, как указано выше, использование вектора (SYMBOL .COUNT) в качестве структуры данных, которую вы передаете вниз, и уменьшение счетчика (на копии!) вместо удаления также должно позаботиться об этом. - person Vatine; 20.01.2010
comment
(defun permutations () (labels ((do-permutations (items) (if items (mapcan (lambda (x) (mapcar (lambda (y) (cons x y)) (do-permutations (remove x items)))) items ) '(())))) (сделай перестановки '(Парень-Чувак)))) - person Carlos Konstanski; 10.02.2018
comment
Нужна ли проверка (нулевого списка)? Я думаю, что условия (null (cdr list)) должно быть достаточно. - person Dietrich; 24.06.2019

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

(defun all-permutations (lst &optional (remain lst))
  (cond ((null remain) nil)
        ((null (rest lst)) (list lst))
        (t (append
            (mapcar (lambda (l) (cons (first lst) l))
                    (all-permutations (rest lst)))
            (all-permutations (append (rest lst) (list (first lst))) (rest remain))))))

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

person Orm Finnendahl    schedule 09.12.2011
comment
может обернуть cond в remove-duplicates, если требуются уникальные перестановки - person mcheema; 03.01.2017

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

Минусы этого элемента для всех перестановок остальных элементов.

person Carl Smotricz    schedule 18.01.2010