Longest Increasing Subsequence

最近「Longest Increasing Subsequence」という問題を知りました。問題の内容は、

 自然数からなるランダムな数列の中から最長の増加部分列(Longest Increasing Subsequence)を探す

という単純なものでグラフ問題の一種だそうです(http://en.wikipedia.org/wiki/Longest_increasing_subsequence_problem)。例えば、次の数列が与えられた場合、

  1 23 14 43 80 42 16 46 58 36

その最長増加部分列は、

  1 14 16 46 58

となります。

今回、この問題を解くプログラムをLongest Increasing Subsequence - Algorithmistで公開されているC++実装を元にSchemeGauche)で書き直してみました。おそらく冗長で非効率な記述が多々あると思うので、どなたか添削していただけると幸甚です。

(use gauche.sequence)

(define (size-of vec) (vector-length vec))

(define (tail-of vec)
  (ref vec (- (size-of vec) 1)))

(define (push vec . obj)
  (list->vector (append (vector->list vec) obj)))

(define (update vec index obj)
  (begin
    (set! (ref vec index) obj)
    vec))

(define (backtrace v1 v2 v3)
  (let loop ((i (- (size-of v2) 1))
	     (j (tail-of v2))
	     (result (list (ref v1 (tail-of v2)))))
    (if (= i 0)
	result
	(let ((k (ref v3 j)))
	  (loop (- i 1) k (cons (ref v1 k) result))))))

(define (loop i v1 v2 v3)
  (let ((j (tail-of v2)))
    (cond ((>= i (size-of v1))
	   (backtrace v1 v2 v3))
	  ((< (ref v1 j) (ref v1 i))
	   (loop (+ i 1) v1 (push v2 i) (update v3 i j)))
	  (else
	   (letrec ((finder
		     (lambda (u v)
		       (let ((c (floor (/ (+ u v) 2))))
			 (cond ((>= u v)
				(if (and (= u v) (< (ref v1 i) (ref v1 (ref v2 u))))
				    (loop (+ i 1) v1 (update v2 u i)
					  (if (> u 0)
					      (update v3 i (ref v2 (- u 1)))
					      v3))
				    (loop (+ i 1) v1 v2 v3)))
			       ((< (ref v1 (ref v2 c)) (ref v1 i))
				(finder (+ c 1) v))
			       (else (finder u c)))))))
	     (finder 0 (- (size-of v2) 1)))))))

;; l.i.s = longest increasing subsequence
(define (find-lis seq)
  (let ((v (list->vector seq)))
    (loop 1 v (vector 0) (make-vector (size-of v)))))

(define (random-number min max)
  (+ (modulo (sys-random) (+ max 1)) min))

(define (make-random-sequence n)
  (let loop ((i 0) (seq '()))
    (if (= i n)
	seq
	(loop (+ i 1) (cons (random-number 1 100) seq)))))

(define (try-to-find-lis n) 
  (begin
    (sys-srandom (sys-time))
    (let ((seq (make-random-sequence n)))
      (format #t "~a \n~a \n" seq (find-lis seq)))))

(define (main args)
  (if (null? (cdr args))
      (try-to-find-lis 100)
      (try-to-find-lis (string->number (cadr args)))))