SICP ex 2.67, 2.68, 2.69, 2.70, 2.71 Huffman符号木
;; Huffman符号木 (define (make-leaf symbol weight) (list 'leaf symbol weight)) (define (leaf? object) (eq? (car object) 'leaf)) (define (symbol-leaf x) (cadr x)) (define (weight-leaf x) (caddr x)) (define (make-code-tree left right) (list left right (append (symbols left) (symbols right)) (+ (weight left) (weight right)))) (define (left-branch tree) (car tree)) (define (right-branch tree) (cadr tree)) (define (symbols tree) (if (leaf? tree) (list (symbol-leaf tree)) (caddr tree))) (define (weight tree) (if (leaf? tree) (weight-leaf tree) (cadddr tree))) (define (decode bits tree) (define (decode-1 bits current-branch) (if (null? bits) '() (let ((next-branch (choose-branch (car bits) current-branch))) (if (leaf? next-branch) (cons (symbol-leaf next-branch) (decode-1 (cdr bits) tree)) (decode-1 (cdr bits) next-branch))))) (decode-1 bits tree)) (define (choose-branch bit branch) (cond ((= bit 0) (left-branch branch)) ((= bit 1) (right-branch branch)) (else (error "bad bit -- CHOOSE-BRANCH" bit)))) (define (adjoin-set x set) (cond ((null? set) (list x)) ((< (weight x) (weight (car set))) (cons x set)) (else (cons (car set) (adjoin-set x (cdr set)))))) (define (make-leaf-set pairs) (if (null? pairs) '() (let ((pair (car pairs))) (adjoin-set (make-leaf (car pair) (cadr pair)) (make-leaf-set (cdr pairs))))))
;; sicp ex 2.67 (define sample-tree (make-code-tree (make-leaf 'A 4) (make-code-tree (make-leaf 'B 2) (make-code-tree (make-leaf 'D 1) (make-leaf 'C 1))))) (define sample-message '(0 1 1 0 0 1 0 1 0 1 1 1 0)) ;; 実行結果 ;; gosh> (decode sample-message sample-tree) ;; (A D A B B C A)
;; sicp ex 2.68 (define (encode message tree) (if (null? message) '() (append (encode-symbol (car message) tree) (encode (cdr message) tree)))) ;; これいらなかった。結局 ;; (define (choose-branch-by-symbol symbol branch) ;; (cond ((element-of-set? symbol (symbols (left-branch branch))) (left-branch branch)) ;; ((element-of-set? symbol (symbols (right-branch branch))) (right-branch branch)) ;; (else (error "bad symbol -- CHOOSE-BRANCH-BY-SYMBOL" symbol)))) (define (element-of-set? symbol symbols) (cond ((null? symbols) #f) ((eq? symbol (car symbols)) #t) (else (element-of-set? symbol (cdr symbols))))) (define (encode-symbol symbol tree) (if (leaf? tree) '() (cond ((element-of-set? symbol (symbols (left-branch tree))) (cons 0 (encode-symbol symbol (left-branch tree)))) ((element-of-set? symbol (symbols (right-branch tree))) (cons 1 (encode-symbol symbol (right-branch tree)))) (else (error "bad symbol -- ENCODE-SYMBOL" symbol)))))
;; sicp ex 2.69 (define (generate-huffman-tree pairs) (successive-merge (make-leaf-set pairs))) ;; 上の successive-merge を実装する ;; pairsには順序づけられた集合が入っている ;; ex ;; gosh> (make-leaf-set '((A 4) (B 2) (C 1) (D 1))) ;; ((leaf D 1) (leaf C 1) (leaf B 2) (leaf A 4)) ;; まず、weightの軽い順に順序づけられた集合でpairsは与えられるので、 ;; 前二つを使ってmake-code-treeによってブランチを作る。 ;; それと残りとの集合をadjoin-setによって得る。この集合はadjoin-setによりまた順序づけらている。 ;; 要素が1つのリストができるまでそれを繰り返せばよい。 ;; よってsuccessive-mergeの実装は以下 (define (successive-merge pairs) (if (null? (cdr pairs)) (car pairs) (successive-merge (adjoin-set (make-code-tree (car pairs) (cadr pairs)) (cddr pairs)))))
;; sicp ex 2.70 (define tree-2.70 (generate-huffman-tree '((A 2) (NA 16) (BOOM 1) (SHA 3) (GET 2) (YIP 9) (JOB 2) (WAH 1)))) (define message-2.70 '(GET A JOB SHA NA NA NA NA NA NA NA NA GET A JOB SHA NA NA NA NA NA NA NA NA WAH YIP YIP YIP YIP YIP YIP YIP YIP YIP SHA BOOM)) ;; 実行結果 ;;gosh> (encode message-2.70 tree-2.70) ;;(1 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 1 0 1 1) ;; 確認 ;;gosh> (define encoded (encode message-2.70 tree-2.70)) ;;encoded ;;gosh> (decode encoded tree-2.70) ;;(GET A JOB SHA NA NA NA NA NA NA NA NA GET A JOB SHA NA NA NA NA NA NA NA NA WAH YIP YIP YIP YIP YIP YIP YIP YIP YIP SHA BOOM) ;; ;; 符号化に何ビット必要か? ;;gosh> (length encoded) ;;84 ;; というわけで、84ビット必要 ;; ;; 固定長符号を使う場合、符号の数が8つなので、1つの符号に3ビット必要。 ;; messageの長さが ;;gosh> (length '(GET A JOB SHA NA NA NA NA NA NA NA NA GET A JOB SHA NA NA NA NA NA NA NA NA WAH YIP YIP YIP YIP YIP YIP YIP YIP YIP SHA BOOM)) ;;36 ;; と36なので ;; 36 * 3 = 108ビット必要 ;; この結果から、可変長符号を使うことで節約ができていることがわかる。約22%の節約。
;; sicp ex 2.71 ;; 1,2,4...2^(n-1) ;; 記号のn番目の相対頻度が2^(n-1)で表されるとき、 ;; n番目の相対頻度は、n-1番目までの相対頻度をすべて足したものよりも大きい。 ;; よって、木は片側にどんどんのびて、各branchのもう片方は絶対にleafになるような木になる。 ;; こういう木の時、最高頻度の記号では1ビットで、最低頻度の記号ではn-1ビット必要になる。
2.72は何となくはわかるものの、ちょっと不鮮明。
もうちょっと考えます。
やる前は、ちょっとできるかなと心配だったけど、
やってみたら思いのほかできた。
よしよし。