You are looking at historical revision 1620 of this page. It may differ significantly from its current revision.
;;;; chase-sequence.scm
;;;; Kon Lovett, Jan '06
;;;; License: BSD
;;; Generates all combinations in near-perfect minimal-change order.
(use lolevel srfi-4 iset)
(eval-when (compile)
(declare
(fixnum)
(unsafe)
)
)
;; Returns the next vector permutation of the specified chase sequence
;; procedure, or #f when no more permutations.
(define (next-chase-permutation cs)
(let ([num (cs 'num)] [pcs (cs)])
(if pcs
(let ([v (make-vector num)])
(do ([i 0 (add1 i)]) ([= i num] v)
(vector-set! v i (pcs i))))
#f)))
;; Enumerate the permutations of a vector, data-vec, at the cut,
;; cut-point. The optional third argument is a procedure of one
;; argument, called for every swap with the index of the swap.
;;
;; Returns a procedure of one, optional, argument.
;; When missing the called procedure will return a
;; procedure of one argument, an fixnum index where 0 <= index <
;; (vector-length data-vec), or #f when permutations exhausted. This
;; procedure when called will return the value of the data-vec at index
;; in the current permutation.
;;
;; When the optional argument is present it must be one of 'swaps, 'num, 'a,
;; or 'data. The corresponding internal variable state will be returned.
;; 'swaps = total # of swaps performed, 'num = length of the data,
;; 'data = original data-vec, 'a = bit-vector where #t bit is a swap.
;;
;; Chase's Sequence is an algorithm for enumerating the permutations of
;; a data-set by adjacent swaps.
;; (Knuth's "The Art of Computer Programming" pre-fascicle 2C, the draft
;; of section 7.2.1.3)
(define (make-chase-sequence data-vec cut-point . swap-proc)
(let ([num (vector-length data-vec)])
(let ([t (if (<= 1 cut-point num) cut-point num)]) ; t = k
(let ([s (- num t)])
(let ([r (if (positive? s) s t)]
[data (object-copy data-vec)]
[swaps 0]
[swap-proc (:optional swap-proc noop)])
(let ([swap-at (lambda (i) (add1 swaps) (swap-proc i))]
[comb (make-u32vector num)]
[ix (make-u32vector num)]
[w (make-bit-vector (add1 num) #t)]
[a (make-bit-vector num)]
[u32vector-swap! (lambda (u32v i j)
(let ([oi (u32vector-ref u32v i)])
(u32vector-set! u32v i (u32vector-ref u32v j))
(u32vector-set! u32v j oi)))])
(do ([i 0 (add1 i)]) ([= i num])
(u32vector-set! comb i i)
(u32vector-set! ix i i)
(bit-vector-set! a i (>= i s)))
(letrec (
;; swap i and (i+1)
[adjacent-swap (lambda (i)
(assert (and (>= i 0) (< i (sub1 num))))
(u32vector-set! ix (u32vector-ref comb i) (add1 i))
(u32vector-set! ix (u32vector-ref comb (add1 i)) i)
(u32vector-swap! comb i (add1 i))
(assert (= (u32vector-ref ix (u32vector-ref comb i)) i))
(assert (= (u32vector-ref ix (u32vector-ref comb (add1 i))) (add1 i)))
(swap-at i))]
;; "bubble" the object at [[from] to [[to] by swapping
[move (lambda (f t)
(assert (and (>= f 0) (< f num)))
(assert (and (>= t 0) (< t num)))
(if (< f t)
(do ([i f (add1 i)]) ([= i t])
(adjacent-swap i))
(do ([i (sub1 f) (sub1 i)]) ([< i t])
(adjacent-swap i))))]
;; move the object at index 'left' to the index immediately left of the cut,
;; and move the object at index 'right' to the index immediate right of the
;; cut
[exchange (lambda (l r)
(assert (and (>= l 0) (< l num)))
(assert (and (>= r 0) (< r num)))
(assert (>= (u32vector-ref ix l) s)) ; currently right of cut
(move (u32vector-ref ix l) s) ; move it to immediate right of cut
(assert (< (u32vector-ref ix r) s)) ; currently left of cut
(move (u32vector-ref ix r) (sub1 s)) ; move it to immediate left of cut
(adjacent-swap (sub1 s)) ; trade sides just across the cut
(assert (bit-vector-ref a l))
(bit-vector-set! a l #f)
(assert (not (bit-vector-ref a r)))
(bit-vector-set! a r #t))]
;; C4
[move-right-one (lambda (j)
(assert (> j 0))
(exchange j (sub1 j))
(cond
((and (= r j)
(> j 1)) (set! r (sub1 j)))
((= r (sub1 j))
(set! r j))))]
;; C5
[move-right-two (lambda (j)
(assert (> j 1))
(if (bit-vector-ref a (- j 2))
(move-right-one j)
(begin
(exchange j (- j 2))
(cond
((= r j)
(set! r (if (> j 3) (- j 2) 1)))
((= r (- j 2))
(set! r (sub1 j)))))))]
;; C6
[move-left-one (lambda (j)
(assert (> j 0))
(exchange (sub1 j) j)
(cond
((and (= r j) (> j 1))
(set! r (sub1 j)))
((= r (sub1 j))
(set! r j))))]
;; C7
[move-left-two (lambda (j)
(assert (> j 0))
(if (bit-vector-ref a (sub1 j))
(move-left-one j)
(begin
(assert (> j 1))
(exchange (- j 2) j)
(cond
((= r (- j 2))
(set! r j))
((= r (sub1 j))
(set! r (- j 2)))))))]
;; retrieve the permuted data at index i
[ref-proc (lambda (i)
(vector-ref data (u32vector-ref comb i)))]
[gen-partition (lambda ()
; C3: Find j and branch
(let ([j #f])
(do ([i r (add1 i)]) ([bit-vector-ref w i] (set! j i))
(bit-vector-set! w i #t))
(if (= j num)
#f
(let ([[aj (bit-vector-ref a j)])
(bit-vector-set! w j #f)
(set! swaps 0)
(if (odd? j)
(if aj
(move-right-one j)
(move-left-two j))
(if aj
(move-right-two j)
(move-left-one j)))
ref-proc))))]
[next-partition (lambda ()
(set! next-partition gen-partition)
ref-proc)])
;; turn this into the next combination. false when there are no more.
(lambda swapf
(switch (:optional swapf #f)
('swaps swaps)
('num num)
('data data)
('a a)
(else (next-partition)))))))))))