【问题标题】:How can I get all possible permutations of a list with Common Lisp?如何使用 Common Lisp 获得列表的所有可能排列?
【发布时间】:2010-01-18 16:55:57
【问题描述】:

我正在尝试编写一个 Common Lisp 函数,该函数将为我提供列表的所有可能排列,每个元素只使用一次。例如,列表 '(1 2 3) 将给出输出 ((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1))。

我已经写了一些这样的作品,但它很笨拙,它并不总是有效,我什至都不真正理解它。我不是要代码,只是可能需要一些关于如何思考它的指导。我对写算法了解不多。

谢谢, 杰森

【问题讨论】:

  • 通常最好发布您目前编写的代码。这样我们就可以看到你的想法……
  • 如果这是作业,请标记它。
  • 这不是家庭作业。我故意省略了到目前为止的代码。我不想用我有缺陷的想法玷污答案。

标签: algorithm lisp common-lisp


【解决方案1】:

作为一种基本方法,“所有排列”遵循这种递归模式:

列表 L 的所有排列是: 对于 L 中的每个元素 E: 该元素添加到 [ L with E removed ] 的所有排列之前

如果我们认为您的列表中没有重复元素,则应该执行以下操作:

(defun all-permutations (list) (cond ((null list) nil) ((null (cdr list)) (list list)) (t (循环列表中的元素 append (mapcar (lambda (l) (cons element l)) (所有排列(删除元素列表)))))))

【讨论】:

  • 谢谢!这有点像我所拥有的,但有一些小而重要的区别。我看到的唯一问题是 (all-permutations '(1 2 3)) 和 (all-permutations '(1 1 2 3)) 给出相同的结果,但这应该很容易改变。 (我在这里的最终目标是打乱字。)
  • 如果你有无法区分的元素,它会变得有点棘手,你需要做一些预处理以避免多次生成“相同”的排列。但是,不要像上面那样使用列表,而是使用 (SYMBOL . COUNT) 的向量作为您传递的数据结构并减少计数(在副本上!)而不是删除也应该注意这一点。
  • (defun permutations () (labels ((do-permutations (items)) (if items (mapcan (lambda (x) (mapcar (lambda (y) (cons x y))) (do-permutations (删除 x 项)))) 项) '(())))) (do-permutations '("Guy" "Man" "Dude")))))
  • 是否需要(空列表)检查?我认为 (null (cdr list)) 条件应该足够了。
【解决方案2】:

这是允许重复元素的答案。由于不使用循环,代码更加“口齿不清”,缺点是比 Rainer Joswig 的解决方案更难理解:

(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))))))

可选的保持参数用于 cdring 列表,在进入递归之前旋转列表元素。

【讨论】:

  • 如果需要唯一的排列,可以将 cond 包装在 remove-duplicates 中
【解决方案3】:

浏览您的列表,依次选择每个元素。该元素将是您当前排列的第一个元素。

将该元素与其余元素的所有排列相结合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2014-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    相关资源
    最近更新 更多