ここに私が思いつくことができるいくつかがありますが、私はそれらのいずれにも満足していません:
(defsubst i-swap (array a b)
(let ((c (aref array a)))
(aset array a (aref array b))
(aset array b c) array))
(defun i-permute-recursive (array offset length)
(if (= offset length)
(message "array: %s" array)
(let ((i offset))
(while (< i length)
(i-permute-recursive (i-swap array i offset) (1+ offset) length)
(i-swap array i offset)
(incf i)))))
(defun i-permute-johnson-trotter (array)
(let ((i 0) largest largest-pos largest-sign swap-to
(markers (make-vector (length array) nil)))
(while (< i (length array))
(aset markers i (cons '1- i))
(incf i))
(setcar (aref markers 0) nil)
(while (some #'car markers)
(setq i 0 largest nil)
(while (< i (length array))
(destructuring-bind (tested-sign . tested-value)
(aref markers i)
(when (and tested-sign
(or (not largest)
(< largest tested-value)))
(setq largest tested-value largest-pos i
largest-sign tested-sign)))
(incf i))
(when largest
(setq swap-to (funcall largest-sign largest-pos))
(i-swap array largest-pos swap-to)
(i-swap markers largest-pos swap-to)
(when (or (= swap-to 0) (= swap-to (1- (length array)))
(> (cdr (aref markers
(funcall largest-sign swap-to)))
largest))
(setcar (aref markers swap-to) nil))
(setq i 0)
(while (< i (length array))
(setq swap-to (cdr (aref markers i)))
(when (> swap-to largest)
(setcar (aref markers i)
(if (< i largest-pos) '1+ '1-)))
(incf i))
(message "array: %s <- makrers: %s" array markers)))))
再帰的なバリアントは余分なスワッピングを行い、再帰的であることは私を非常に不幸にします (私はデバッグのしやすさに関心があるので、スタックのサイズには関心がありません - 再帰関数はデバッガでひどく見えます...)
Wiki の説明から実装した他のバージョン。興味がある場合はこちら: http://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithmしかし、どちらも長すぎます (ちょうどコード自体は非常に長いです)、それは多かれ少なかれ O(n*m) です。これは、短い配列の場合、ほぼ 2 次のようになります。(m は配列の長さ、n は順列の数です。)
再帰バージョンを見ると、*プレーン* O(n) バリアントが必要になることを願っていますが、頭を包むことはできません...
別の Lisp で書く方が快適だと思うなら、大歓迎です!