排列的方案代码的说明

问题描述

这是一个Scheme代码,用于产生元素列表的排列:

 (define (remove x lst)   (cond
     ((null? lst) '())
     ((= x (car lst)) (remove x (cdr lst)))
     (else (cons (car lst) (remove x (cdr lst))))))

 (define (permute lst)   (cond
     ((= (length lst) 1) (list lst))
     (else (apply append
            (map (lambda (i) 
             (map (lambda (j) (cons i j))
                  (permute (remove i lst)))) lst)))))

如果我们将代码分开,我确实会理解代码的每个部分,但是我不明白的是这一切如何导致生成排列?

让我们假设列表‘(a b),它如何生成‘(a b)‘(b a)

我们首先从列表中删除a并留下b,但是现在写在哪里,您必须将a限制为bb是一个元素,但是在我对代码的解释中,b也将被删除,并且什么也没有留下……

解决方法

我会这样阅读主体部分(按数字指示的顺序)

   (map (lambda (i)     ;(1) for each i in...
      (map (lambda (j)  ;(3) for each j in...
           (cons i j))  ;(6) append i to front (of this shorter permutation)
         (permute       ;(4) ...the list of all permutations...
           (remove i lst))))    ;(5) ...of the input list with i removed
    lst)                ;(2) ... the input list
,

(apply append (map f xs)) == (flatmap f xs)

用方程式模式匹配伪代码重新编写代码,

remove x [x,...ys] = remove x ys           ; skip this x,and go on removing
            ; ( consider skipping just this one occurrence instead:
            ;       = ys                                           )
remove x [y,...ys] = [y,...remove x ys]   ;   (or else x /= y,so keep this y)
remove x [] = []                            ; until the list is exhausted

permute [x] = [[x]]
permute xs  = 
    xs                                ; ( with (xs |> f) == (f xs) )
    |> flatmap (x =>                  ; for each x in xs,permute (remove x xs)       ;   for each permutation p of xs w/o x,|> map (p => [x,...p]) )   ;      prepend x to p and 
                                      ;   splice the results in place of x

这个清楚吗?

不?那么,让我们看看如何计算permute [a,b]

首先,permute [a]是什么?

  permute [a] = ...
( permute [x] = [[x]] )
          ... = [[a]]

(我们如何调用单元素列表的第一个元素并不重要,它仍然是它的第一个也是唯一的元素)。同样,

  permute [b] = ...
( permute [x] = [[x]] )
          ... = [[b]]

好的,但是它如何帮助我们看到permute [a,b]的结果呢?让我们逐步进行操作:

permute [      a,b       ] =
   ;; for each x in (xs==[a,b])
   ;;          a           b           ; <<- the value of x
   ;;      remove x from xs
   ;;         [b]         [a]          ; <<- xs with x removed
   ;;      prepend x to each permutation of the above
   ;;       [[  b]]     [[  a]]        ; <<- permutations
   ;;       [[a,b]]     [[b,a]]        ; <<- prefixed with x
   ;;      splice them in by `apply append`
        [    [a,b],[b,a]     ]

然后,permute [b,c] == [[b,c],[c,b]]等。并且,有了这些知识,

permute [        a,b,c          ] =
   ;; for each x in (xs==[a,c])
   ;;      remove x from xs
   ;;          [b,c]              [a,b]
   ;;      prepend x to each permutation of the above
   ;;    [[  b,[  c,b]]  [[  a,a]]  [[  a,[  b,a]]
   ;;    [[a,[a,c,b]]  [[b,a,a]]  [[c,a]]
   ;;      splice them in by `apply append`
        [ [a,a],a]  ]

更清晰吗?

,

TL; DR:口头解释在此答案的最后。)

让我们尝试遵循let*-重写的定义。定义是

(define (remove x lst)   (cond
    ((null? lst) '())
    ((= x (car lst)) (remove x (cdr lst)))
    (else (cons (car lst) (remove x (cdr lst))))))

(define (permute lst)   (cond
    ((= (length lst) 1) (list lst))
    (else (apply append
           (map (lambda (i) 
            (map (lambda (j) (cons i j))
                 (permute (remove i lst)))) lst)))))

我们尝试

(permute '(a b))
≡
(let* ((lst '(a b)))
  (apply append
     (map (lambda (i)
             (map (lambda (j) (cons i j))
                  (permute (remove i lst))))
          lst)))
≡
(let* ((lst '(a b))
       (r  (map (lambda (i)
                  (map (lambda (j) (cons i j))
                       (permute (remove i lst))))
                lst)))
  (apply append r))
≡
(let* ((lst '(a b))
       (i1 'a)
       (r1 (map (lambda (j) (cons i1 j))
                       (permute (remove i1 lst))))
       (i2 'b)
       (r2 (map (lambda (j) (cons i2 j))
                       (permute (remove i2 lst))))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((lst '(a b))
       (i1 'a)
       (t1 (permute (remove i1 lst)))
       (r1 (map (lambda (j) (cons i1 j)) t1))
       (i2 'b)
       (t2 (permute (remove i2 lst)))
       (r2 (map (lambda (j) (cons i2 j)) t2))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((i1 'a)
       (t1 (permute '(b)))
       (r1 (map (lambda (j) (cons i1 j)) t1))
       (i2 'b)
       (t2 (permute '(a)))
       (r2 (map (lambda (j) (cons i2 j)) t2))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((i1 'a)
       (t1 '( (b) ))
       (r1 (map (lambda (j) (cons i1 j)) t1))
       (i2 'b)
       (t2 '( (a) ))
       (r2 (map (lambda (j) (cons i2 j)) t2))
       (r (list r1 r2)))
  (apply append r))

所以我们得到

(let* ((r1 (map (lambda (j) (cons 'a j)) '( (b) )))
       (r2 (map (lambda (j) (cons 'b j)) '( (a) )))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((r1 (list (cons 'a '(b))))
       (r2 (list (cons 'b '(a))))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((r1 (list '(a b)))
       (r2 (list '(b a)))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((r1  '((a b)))
       (r2  '((b a)))
       (r (list r1 r2)))
  (apply append r))
≡
(apply append (list '((a b)) '((b a))))
≡
(      append       '((a b)) '((b a)) )
≡
'(                    (a b)    (b a)  )

如果您需要使自己确信中间结果的有效性,请遵循相同的技术。


事后看来,我们可以更积极地简化它,例如

(let* ((lst '(a b))
       (i1 'a)
       (r1 (map (lambda (j) (cons i1 j))
                       (permute (remove i1 lst))))
       (i2 'b)
       (r2 (map (lambda (j) (cons i2 j))
                       (permute (remove i2 lst))))
       (r (list r1 r2)))
  (apply append r))
≡
(let* ((lst '(a b))
       (i1 'a)
       (t1 (permute (remove i1 lst)))
       (r1 (map (lambda (j) (cons i1 j)) t1))
       (i2 'b)
       (t2 (permute (remove i2 lst)))
       (r2 (map (lambda (j) (cons i2 j)) t2)))
  (apply append (list r1 r2)))
≡
(let* ((t1 (permute '(b)))
       (r1 (map (lambda (j) (cons 'a j)) t1))
       (t2 (permute '(a)))
       (r2 (map (lambda (j) (cons 'b j)) t2)))
  (append r1 r2))
≡
(let* ((r1 (map (lambda (j) (cons 'a j)) '( (b) )))
       (r2 (map (lambda (j) (cons 'b j)) '( (a) )))
       )
  (append r1        ; one row for each elt            '( a
          r2        ;  of the input list,b
          ))        ;  spliced in place by append        )

等,最后以更直观的方式显示计算的结构:

  • 对于输入列表的每个元素,
    • 找到其余的所有排列,
    • 将该元素添加到每个元素之前,
  • 并通过将所有结果附加在一起,从而将由此处理输入列表中的每个元素所得到的结果结合在一起。

(因此在这里证明我的其他基于伪代码的answer的正确性)。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...