Archive for 程序园

SICP习题解答:第二章(上)

?View Code SCHEME
;; 2.1
(define (make-rat n d)
  (if (< d 0)
      (make-rat (- n) (- d))
      (cons n d)))
 
;; 2.2
(define (make-point x y)
  (cons x y))
(define (x-point p)
  (car p))
(define (y-point p)
  (cdr p))
(define (make-segment start end)
  (cons start end))
(define (start-segment s)
  (car s))
(define (end-segment s)
  (cdr s))
(define (midpoint-segment s)
  (let ((a (start-segment s))
        (b (end-segment s)))
    (make-point (/ (+ (x-point a) (x-point b)) 2)
                (/ (+ (y-point a) (y-point b)) 2))))
 
;; 2.3
 
; 第一种表示
(define (make-rect mid-point width height)
  (cons mid-point
        (cons (/ width 2) (/ height 2))))
(define (rect-left r)
  (- (x-point (car r)) (car (cdr r))))
(define (rect-right r)
  (+ (x-point (car r)) (car (cdr r))))
(define (rect-bottom r)
  (- (y-point (car r)) (cdr (cdr r))))
(define (rect-top r)
  (+ (y-point (car r)) (cdr (cdr r))))
 
; 第二种表示
(define (make-rect bottom-left top-right)
  (cons bottom-left top-right))
(define (rect-left r)
  (x-point (car r)))
(define (rect-right r)
  (x-point (cdr r)))
(define (rect-bottom r)
  (y-point (car r)))
(define (rect-top r)
  (y-point (cdr r)))
 
; 公用函数
(define (rect-width r)
  (- (rect-right r) (rect-left r)))
(define (rect-height r)
  (- (rect-top r) (rect-bottom r)))
(define (rect-area r)
  (* (rect-width r) (rect-height r)))
(define (rect-perimeter r)
  (* 2 (+ (rect-width r) (rect-height r))))
 
;; 2.4
(define (my-cons x y)
  (lambda (m) (m x y)))
(define (my-car z)
  (z (lambda (p q) p)))
(define (my-cdr z)
  (z (lambda (p q) q)))
 
;; 2.5
(define (cons-n x y) ; x and y are natural numbers
  (if (= x 0)
      (if (= y 0) 1
          (* 3 (cons x (- y 1))))
      (* 2 (cons (- x 1) y))))
(define (car-n z)
  (if (= 0 (remainder z 2))
      (+ 1 (car (/ z 2)))
      0))
(define (cdr-n z)
  (if (= 0 (remainder z 3))
      (+ 1 (cdr (/ z 3)))
      0))
 
;; 2.6 目前最激动人心的题目
(define zero
  (lambda (f) (lambda (x) x)))
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))
(define one
  (lambda (f) (lambda (x) (f x))))
(define two
  (lambda (f) (lambda (x) (f (f x)))))
(define (add m n)
  (lambda (f) (lambda (x) ((m f) ((n f) x)))))
 
;; 2.7 为什么这么重复的题目啊
(define (make-interval a b) (cons a b))
(define (lower-bound x) (car x))
(define (upper-bound x) (cdr x))
 
;; 2.8
(define (sub-interval x y)
  (make-interval (- (lower-bound x) (upper-bound y))
                 (- (upper-bound x) (lower-bound y))))
 
;; 2.9 对于加法,结果的上(下)界即两上(下)界之和,故宽度就是两宽度之和,即(a-b)+(c-d)=(a-c)-(b-d)。减法有相同的结论。对于乘法,(a-b)*(c-d)!=(ac-bd);例如宽度相同的(0,1)、(1,2)、(2,3)两两做乘法运算得到(0,2)、(0,3)、(2,6)三个宽度不同的区间。除法类似。
 
;; 2.10
(define (div-interval x y)
  (if (and (< (lower-bound y) 0) (< 0 (upper-bound)))
      (error "Divide by a interval that contains zero -- div-interval")
      (mul-interval x
                    (make-interval (/ 1 (upper-bound y))
                                   (/ 1 (lower-bound y))))))
 
;; 2.11 其实归结起来只有三种情况:0、1或2个区间包括0。
(define (print-interval i)
  (display "[")
  (display (lower-bound i))
  (display ",")
  (display (upper-bound i))
  (display "]\n"))
(define (neg-interval i)
  (make-interval (- (lower-bound i)) (- (upper-bound i))))
(define (mul-interval x y)
  (define (do-it a b c d)
    (cond ((< b 0) (neg-interval(do-it (- b) (- a) c d)))
          ((< d 0) (neg-interval(do-it a b (- d) (- c))))
          ((and (> a 0) (< c 0)) (do-it c d a b))
          ((and (< a 0) (> c 0)) (make-interval (* a d) (* b d)))
          ((and (< a 0) (< c 0)) (make-interval 0 (max (* a c) (* b d))))
          ((and (> a 0) (> c 0)) (make-interval (* a c) (* b d))))
    )
  (let ((a (lower-bound x))
        (b (upper-bound x))
        (c (lower-bound y))
        (d (upper-bound y)))
    (do-it a b c d)))
;;;
(define (mul-interval-test-cases)
  (print-interval (mul-interval (make-interval 2 3) (make-interval 4 5)))
  (print-interval (mul-interval (make-interval 2 3) (make-interval -4 5)))
  (print-interval (mul-interval (make-interval 2 3) (make-interval -5 -4)))
  (print-interval (mul-interval (make-interval -2 3) (make-interval 4 5)))
  (print-interval (mul-interval (make-interval -2 3) (make-interval -4 5)))
  (print-interval (mul-interval (make-interval -2 3) (make-interval -5 -4)))
  (print-interval (mul-interval (make-interval -3 -2) (make-interval 4 5)))
  (print-interval (mul-interval (make-interval -3 -2) (make-interval -4 5)))
  (print-interval (mul-interval (make-interval -3 -2) (make-interval -5 -4))))
 
;; 2.12
(define (make-center-percent center percent)
  (define delta (/ (* center percent) 100))
  (make-interval (- center percent) (+ center percent)))
(define (center i)
  (/ (+ (lower-bound i) (upper-bound i)) 2))
(define (percent i)
  (abs (* 50 (/ (- (upper-bound i) (lower-bound i)) (center i)))))
(define (print-center-percent i)
  (display (center i))
  (display "±")
  (display (percent i))
  (newline)
  )
 
;; 2.13 a(1+b%)*c(1+d%)=ac(1+b%+d%+b%d%)~=ac(1+b%+d%)
 
;; 2.14 程序将 (div-interval A A) 与 (div-interval A B) 做相同的处理,而没有意识到前者的两个参数并非是独立的。也即,当同一个变量在式子中多次出现时,程序不能意识到它的每次出现必须取相同的值。
(define (check-interval-arithmetic)
  (let ((A (make-interval 2 3))
        (B (make-interval 2 3)))
    (print-interval (div-interval A A))
    (print-interval (div-interval A B))))
(define (check-interval-arithmetic-center-percent)
  (let ((A (make-center-percent 100.0 3))
        (B (make-center-percent 100.0 3)))
    (print-center-percent (div-interval A A))
    (print-center-percent (div-interval A B))))
 
;; 2.15 我认为这公式的两个形式是完全等价的,在Alyssa系统上产生的区间限界不同,是由于Alyssa系统的本质缺陷,而非两个形式的优劣。
 
;; 2.16 当同一个变量在式子中多次出现时,程序不能意识到它的每次出现必须取相同的值。每个变量出现的次数不同会导致Alyssa系统得出的结果不同。无缺陷的区间算术包等价于以下问题:多变量的多项式,每个变量有其取值范围,求多项式的值的取值范围。这个问题属于 Interval Computation 的范畴:http://www.lsi.upc.edu/~robert/mirror/interval-comp/
 
;; 2.17
(define (last-pair l)
  (cond ((null? (cdr l)) l)
        (else (last-pair (cdr l)))))
 
;; 2.18
(define (reverse l)
  (define (do-it a b)
    (if (null? a) b
        (do-it (cdr a) (cons (car a) b))))
  (do-it l nil))
 
;; 2.19
(define (cc amount coin-values)
  (cond ((= amount 0) 1)
        ((or (< amount 0) (no-more? coin-values)) 0)
        (else
         (+ (cc amount
                (except-first-denomination coin-values))
            (cc (- amount
                   (first-denomination coin-values))
                coin-values)))))
(define (no-more? l) (null? l))
(define (except-first-denomination l) (cdr l))
(define (first-denomination l) (car l))
; 根据问题的定义,改变表中元素的顺序不会影响回答。
 
;; 2.20
(define (same-parity x . s)
  (define (iter a)
    (if (null? a) nil
        (let ((b (iter (cdr a))))
          (if (even? (- x (car a)))
              (cons (car a) b) b))))
  (cons x (iter s)))
 
;; 2.21
(define (square-list items)
  (if (null? items)
      nil
      (cons (square (car items)) (square-list (cdr items)))))
 
(define (square-list items)
  (map square items))
 
;; 2.22 第一个程序中,程序从前往后处理things中的元素,却每次把平方后的元素加到answer的头部,故结果的顺序会相反。第二个程序中,当cons的两个参数分别是list和元素时,结果并不是把元素加到list后面。
 
;; 2.23
(define (for-each f l)
  (cond ((not (null? l))
         (f (car l))
         (for-each f (cdr l)))))
 
;; 2.24 (1 (2 (3 4)))。
 
;; 2.25
(car (cdr (car (cdr (cdr '(1 3 (5 7) 9))))))
(car (car '((7))))
(car (cdr (car (cdr (car (cdr (car (cdr (car (cdr (car (cdr '(1 (2 (3 (4 (5 (6 7))))))))))))))))))
 
;; 2.26
; (1 2 3 4 5 6)
; ((1 2 3) 4 5 6)
; ((1 2 3) (4 5 6))
 
;; 2.27
(define (deep-reverse t)
  (if (list? t)
      (map deep-reverse (reverse t))
      l))
 
;; 2.28
(define (fringe t)
  (cond ((null? t) nil)
        ((list? t) (append (fringe (car t)) (fringe (cdr t))))
        (else (list t))))
 
;; 2.29
(define (make-mobile left right)
  (list left right))
(define (make-branch length structure)
  (list length structure))
 
; (a)
(define (left-branch mobile)
  (car mobile))
(define (right-branch mobile)
  (cadr mobile))
(define (branch-length branch)
  (car branch))
(define (branch-structure branch)
  (cadr branch))
 
; (b)
(define (total-weight x)
  (if (number? x)
      x
      (+ (total-weight (branch-structure (left-branch x))) (total-weight (branch-structure (right-branch x))))))
 
;  (c)
(define (branch-torque x) 
  (* (total-weight (branch-structure x)) 
     (branch-length x)))
 
(define (balanced? x)
  (cond ((number? x) true)
        (else
         (let ((left (left-branch x))
               (right (right-branch x)))
           (and (= (branch-torque left) (branch-torque right)) (balanced? (branch-structure left)) (balanced? (branch-structure right)))))))
 
; (d) 只需更改right-branch和branch-structure中的cadr为cdr
 
;; 2.30
; 直接定义
(define (square-tree t)
  (cond ((null? t) nil)
        ((number? t) (square t))
        (else (cons (square-tree (car t)) (square-tree (cdr t))))))
 
; 使用高阶函数
(define (square-tree t)
  (map (lambda (x)
         (if (list? x)
             (square-tree x)
             (square x)))
       t))
 
;; 2.31
(define (tree-map f t)
  (map (lambda (x)
         (if (list? x)
             (tree-map x)
             (f x)))
       t))
 
;; 2.32 将子集分为包含与不包含(car s)两种情况。
(define (subsets s)
  (if (null? s)
      (list nil)
      (let ((rest (subsets (cdr s))))
        (append rest (map (lambda (x) (cons (car s) x)) rest)))))
 
;; 2.33
(define (unary-map p sequence)
  (accumulate (lambda (x y) (cons (p x) y)) nil sequence))
(define (apppend seq1 seq2)
  (accumulate cons seq2 seq1))
(define (length sequence)
  (accumulate (lambda (x y) (+ 1 y)) 0 sequence))
 
;; 2.34
(define (horner-eval x coefficient-sequence)
  (accumulate (lambda (this-coeff higher-terms) (+ this-coeff (* x higher-terms)))
              0
              coefficient-sequence))
 
;; 2.35
(define (count-leaves t)
  (accumulate (lambda (x y)
                (+
                 (if (list? x) (count-leaves x) 1)
                 y))
              0
              t))
; 或
(define (count-leaves t)
  (accumulate + 0 (map (lambda (x) (if (list? x) (count-leaves x) 1)) t)))
 
;; 2.36
(define (accumulate-n op init seqs)
  (if (null? (car seqs))
      nil
      (cons (accumulate op init (map car seqs))
            (accumulate-n op init (map cdr seqs)))))
 
;; 2.37
(define (dot-product v w)
  (accumulate + 0 (map * v w)))
(define (matrix-*-vector m v)
  (map (lambda (x) (dot-product x v)) m))
(define (transpose mat)
  (accumulate-n cons nil mat))
(define (matrix-*-matrix m n)
  (let ((cols (transpose n)))
    (map (lambda (x) (matrix-*-vector cols x)) m)))
 
;; 2.38
(define (fold-left op initial sequence)
  (define (iter result rest)
    (if (null? rest)
        result
        (iter (op result (car rest))
              (cdr rest))))
  (iter initial sequence))
(define fold-right accumulate)
; 3/2
; 1/6
; (1 (2 (3 ())))
; (((() 1) 2) 3)
; 结合律,以及inital是op的零元。
 
;; 2.39
(define (reverse sequence)
  (fold-right (lambda (x y) (append y (list x))) nil sequence))
(define (reverse sequence)
  (fold-left (lambda (x y) (cons y x)) nil sequence))
 
;; 2.40
(define (flatmap proc seq)
  (accumulate append nil (map proc seq)))
(define (unique-pairs n)
  (flatmap
   (lambda (i) 
     (map
      (lambda (x) (cons x i))
      (enumerate-interval 1 (- i 1))))
   (enumerate-interval 1 n)))
 
;; 2.41
(define (pair-sum-no-more-than n)
  (flatmap
   (lambda (x)
     (map (lambda (y) (cons y x))
          (enumerate-interval 1 (min (- x 1) (- n x)))))
   (enumerate-interval 1 (- n 1))))
(define (triple-sum-is n)
  (filter
   (lambda (x) (not (or (= (car x) (cadr x)) (= (car x) (caddr x)))))
   (map
    (lambda (x) (list (- n (car x) (cdr x)) (car x) (cdr x)))
    (pair-sum-no-more-than (- n 1)))))
 
;; 2.42
(define empty-board nil)
(define (safe? k p)
  (define (not-same p)
    (null? (filter (lambda (x) (= (car p) x)) (cdr p))))
  (and
   (not-same p)
   (not-same (map + p (enumerate-interval 1 k)))
   (not-same (map + p (reverse (enumerate-interval 1 k))))))
(define (adjoin-position n k r)(cons n r))
(define (queens board-size)
  (define (queen-cols k)
    (if (= k 0)
        (list empty-board)
        (filter
         (lambda (positions) (safe? k positions))
         (flatmap
          (lambda (rest-of-queens)
            (map (lambda (new-row)
                   (adjoin-position new-row k rest-of-queens))
                 (enumerate-interval 1 board-size)))
          (queen-cols (- k 1))))))
  (queen-cols board-size))
 
;; 2.43 原程序中每个 (queen-cols k) 只需调用一次,而这个程序中,为了计算 (queen-cols k),需要调用 k 次 (queen-cols (- k 1));大约需要k!T的时间。
 
;; 2.44
(define (up-split painter n)
  (if (= n 0)
      painter
      (let ((smaller (up-split painter (- n 1))))
        (below painter (beside smaller smaller)))))
 
;; 2.45
(define (split combo1 combo2)
  (lambda (painter n)
    (if (= n 0)
        painter
        (let ((smaller (up-split painter (- n 1))))
          (combo1 painter (combo2 smaller smaller))))))
 
;; 2.46
(define (make-vect x y)
  (cons x y))
(define (xcor-vect v)
  (car v))
(define (ycor-vect v)
  (cdr v))
(define (add-vect v w)
  (make-vect (+ (xcor-vect v) (xcor-vect w))
             (+ (ycor-vect v) (ycor-vect w))))
(define (sub-vect v w)
  (make-vect (- (xcor-vect v) (xcor-vect w))
             (- (ycor-vect v) (ycor-vect w))))
(define (scale-vect s v)
  (make-vect (* s (xcor-vect v))
             (* s (ycor-vect v))))
 
;; 2.47
(define (make-frame origin edge1 edge2)
  (list origin edge1 edge2))
(define (origin-frame frame)
  (car frame))
(define (edge1-frame frame)
  (cadr frame))
(define (edge2-frame frame)
  (caddr frame))
 
(define (make-frame origin edge1 edge2)
  (cons origin (cons edge1 edge2)))
(define (edge2-frame frame)
  (cddr frame))
 
;; 2.48
(define (make-segment v w)
  (cons v w))
(define (start-segment s)
  (car s))
(define (end-segment s)
  (cdr s))
 
;; 2.49
; (a)
(define outline-painter
  (let ((left-bottom (make-vect 0 0))
        (left-top (make-vect 0 1))
        (right-bottom (make-vect 1 0))
        (right-top (make-vect 1 1)))
    (segments->painter (list
                        (make-segment left-top left-bottom)
                        (make-segment left-bottom right-bottom)
                        (make-segment right-bottom right-top)
                        (make-segment right-top left-top)))))
; (b)
(define X-painter
  (let ((left-bottom (make-vect 0 0))
        (left-top (make-vect 0 1))
        (right-bottom (make-vect 1 0))
        (right-top (make-vect 1 1)))
    (segments->painter (list
                        (make-segment left-bottom right-top)
                        (make-segment left-top right-bottom)))))
; (c)
(define diamond-painter
  (let ((bottom (make-vect 0.5 0))
        (left (make-vect 0 0.5))
        (top (make-vect 0.5 1))
        (right (make-vect 1 0.5)))
    (segments->painter (list
                        (make-segment bottom left)
                        (make-segment left top)
                        (make-segment top right)
                        (make-segment right bottom)))))
; (d) 来源 http://d.hatena.ne.jp/laughing/20081115/1226737187,有修正
(define wave
  (let ((p0 (make-vect 0.4 1.0))
        (p1 (make-vect 0.35 0.8))
        (p2 (make-vect 0.4 0.6))
        (p3 (make-vect 0.3 0.6))
        (p4 (make-vect 0.15 0.55))
        (p5 (make-vect 0 0.8))
        (p6 (make-vect 0 0.6))
        (p7 (make-vect 0.15 0.4))
        (p8 (make-vect 0.3 0.55))
        (p9 (make-vect 0.35 0.5))
        (p10 (make-vect 0.3 0))
        (p11 (make-vect 0.4 0))
        (p12 (make-vect 0.5 0.3))
        (p13 (make-vect 0.6 0))
        (p14 (make-vect 0.7 0))
        (p15 (make-vect 0.6 0.45))
        (p16 (make-vect 1.0 0.2))
        (p17 (make-vect 1.0 0.4))
        (p18 (make-vect 0.8 0.6))
        (p19 (make-vect 0.6 0.6))
        (p20 (make-vect 0.65 0.8))
        (p21 (make-vect 0.6 1.0)))
    (segments->painter 
     (list
      (make-segment p0 p1)
      (make-segment p1 p2)
      (make-segment p2 p3)
      (make-segment p3 p4)
      (make-segment p4 p5)
      (make-segment p6 p7)
      (make-segment p7 p8)
      (make-segment p8 p9)
      (make-segment p9 p10)
      (make-segment p11 p12)
      (make-segment p12 p13)
      (make-segment p14 p15)
      (make-segment p15 p16)
      (make-segment p17 p18)
      (make-segment p18 p19)
      (make-segment p19 p20)
      (make-segment p20 p21)))))
 
;; 2.50
(define (flip-horiz painter)
  (transform-painter painter
                     (make-vect 1 0)
                     (make-vect 0 0)
                     (make-vect 1 1)))
(define (rotate-180 painter)
  (transform-painter painter
                     (make-vect 1 1)
                     (make-vect 0 1)
                     (make-vect 1 0)))
(define (rotate-270 painter)
  (transform-painter painter
                     (make-vect 0 1)
                     (make-vect 0 0)
                     (make-vect 1 1)))
 
;; 2.51
(define (below painter1 painter2)
  (let ((paint-bottom
         (transform-painter painter1
                            (make-vect 0 0)
                            (make-vect 1 0)
                            (make-vect 0 0.5)))
        (paint-top
         (transform-painter painter2
                            (make-vect 0 0.5)
                            (make-vect 1 0.5)
                            (make-vect 0 1))))
    (lambda (frame)
      (paint-bottom frame)
      (paint-top frame))))
(define (below painter1 painter2)
  (rotate-270 (beside (rotate-90 painter1) (rotate-90 painter2))))
 
;; 2.52
; (a) 略
; (b)
(define (corner-split painter n)
  (if (= n 0)
      painter
      (let ((up (up-split painter n))
            (right (right-split painter n)))
        (let ((top-left up)
              (bottom-right right)
              (corner (corner-split painter (- n 1))))
          (beside (below painter top-left)
                  (below bottom-right corner))))))
; (c)
(define (square-limit painter n)
  (let ((combine4 (square-of-four identity flip-horiz
                                  flip-vert rotate180)))
    (combine4 (corner-split painter n))))

Comments (11)

SICP习题解答:第一章(下)

?View Code SCHEME
;; 1.29
(define (simpson-integral f a b n)
  (define (coefficient i)
    (cond ((or (= i 0) (= i n)) 1)
          ((even? i) 2)
          (else 4)))
  (let ((h (/ (- b a)n )))
    (* (/ h 3)
       (sum (lambda (i) (* (f (+ a (* i h))) (coefficient i)))
            0
            (lambda (i) (+ i 1))
            n))))
 
;; 1.30
(define (sum term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (+ result (term a)))))
  (iter a 0))
 
;; 1.31 只需要把sum改动两处。
; 递归实现
(define (product term a next b)
  (if (> a b)
      1
      (* (term a)
         (product term (next a) next b))))
 
; 迭代实现
(define (product term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (* result (term a)))))
  (iter a 1))
 
; 阶乘
(define (factorial n)
  (product (lambda (x) x) 1 (lambda (i) (+ 1 i)) n))
 
; π的近似值
(define (calc-pi n)
  (define (term i)
    (/ (- (square i) 1) (square i)))
  (define (next i)
    (+ 2 i))
  (* 4 (product term 3.0 next n)))
 
;; 1.32
(define (accumulate combiner null-value term a next b)
  (if (> a b)
      null-value
      (combiner (term a)
                (accumulate combiner null-value term (next a) next b))))
 
(define (accumulate combiner null-value term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (combiner result (term a)))))
  (iter a null-value))
 
;; 1.33
; filtered-accumulate 的两种实现
(define (filtered-accumulate combiner null-value term a next b filter)
  (define (do-filter x)
    (if (filter x) x null-value))
  (if (> a b)
      null-value
      (combiner (do-filter (term a))
                (filtered-accumulate combiner null-value term (next a) next b filter))))
 
(define (filtered-accumulate combiner null-value term a next b filter)
  (define (do-filter x)
    (if (filter x) x null-value))
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (combiner result (do-filter (term a))))))
  (iter a null-value))
 
; (a)
(filtered-accumulate + 0 (lambda (x) x) a (lambda (i) (+ 1 i)) b prime?)
; (b)
(filtered-accumulate * 1 (lambda (x) x) 1 (lambda (i) (+ 1 i)) (- 1 n) (lambda (a) (= 1 (gcd a n))))
 
;; 1.34 (f f)相当于(f 2),相当于(2 2),会因为2并非可以调用的函数而出错。
 
;; 1.35 证明不动点直接计算即可,计算的(fixed-point (lambda (x) (+ 1 (/ 1 x))) 1.0)。
 
;; 1.36 全部代码如下
(define tolerance 0.00001)
 
(define (fixed-point f first-guess)
  (define (close-enough? v1 v2)
    (< (abs (- v1 v2)) tolerance))
  (define (try guess)
    (display guess)
    (newline)
    (let ((next (f guess)))
      (if (close-enough? guess next)
          next
          (try next))))
  (try first-guess))
 
(define (f x) (/ (log 1000) (log x)))
 
(display (fixed-point f 2))
(newline) (newline)
(display (fixed-point (lambda (x) (average x (f x))) 2))
(newline) (newline)
 
; 使用与不使用平均阻尼的步数分别为9与34步。
 
;; 1.37
; 递归版本
(define (cont-frac n d k)
  (define (shift f)
    (lambda (i) (f (+ 1 i))))
  (if (= 0 k)
      0
      (/ (n 1) (+ (d 1) (cont-frac (shift n) (shift d) (- k 1))))))
 
; 迭代版本
(dsefine (cont-frac n d k)
  (define (iter result i)
    (if (= 0 i)
        result
        (iter (/ (n i) (+ (d i) result)) (- i 1))))
  (iter 0 k))
 
; 当k不小于11时,结果有4位精度。
 
;; 1.38
(define (e-2 k)
  (cont-frac (lambda (i) 1.0)
             (lambda (i)
               (if (= 2 (remainder i 3))
                   (* 2 (/ (+ 1 i) 3))
                   1))
             k))
 
;; 1.39
(define (tan-cf x k)
  (cont-frac (lambda (i)
               (if (= 1 i) x (- (square x))))
             (lambda (i)
               (- (* 2 i) 1))
             k))
 
;; 1.40
(define (cubic a b c)
  (lambda (x)
    (+ c (* x (+ b (* x (+ a x)))))))
 
;; 1.41
; double函数
(define (double f)
  (lambda (x)
    (f (f x))))
 
;; (((double (double double)) inc) 5) 的值为21。因为 (double double) 得到一个把参数过程应用四次的函数,而 (double (double double)) 事实上是将“把参数过程应用四次”的过程应用两次,也就是一个将参数过程应用十六次的函数。
 
;; 1.42
(define (compose f g)
  (lambda (x) (f (g x))))
 
;; 1.43
(define (repeated f n)
  (if (= 1 n)
      f
      (compose f (repeated f (- n 1)))))
 
;; 1.44
(define (n-times-smooth f dx n)
  ((repeated (lambda (f) (smooth f dx)) n) f))
 
;; 1.45
(define (calc-nth-root-with-t-average-damps x n t)
  (fixed-point ((repeated average-damp t) (lambda (y) (/ x (fast-expt y (- n 1))))) 1.0))
 
;; 通过实验,得到的规律是,当 2^t>=n 时,用 t 次平均阻尼求 n 次方根是可行的,于是有下面的程序。
(define (nth-root x n)
  (define (minimal-t n)
    (if (= 1 n)
        0
        (+ 1 (minimal-t (quotient n 2)))))
  (calc-nth-root-with-t-average-damps x n (minimal-t n)))
 
;; 1.46
(define (iterative-improve guess improve good-enough?)
  (if (good-enough? guess)
      guess
      (iterative-improve (improve guess) improve good-enough?)))
 
(define (sqrt x)
  (define (improve y)
    (/ (+ x (square y)) (* 2 y)))
  (define (good-enough? y)
    (> tolerance (abs (- (square y) x))))
  (iterative-improve 1.0 improve good-enough?))
 
(define (fixed-point f first-guess)
  (define (improve x)
    (f x))
  (define (good-enough? x)
    (> tolerance (abs (- (f x) x))))
  (iterative-improve first-guess improve good-enough?))

Comments (2)

SICP习题解答:第一章(上)

?View Code SCHEME
;; 1.1-2 略
 
;; 1.3
(define (greater-two a b c)
  (-
   (+ a b c)
   (min a b c)))
; 或
(define (greater-two a b c)
  (cond ((and (> b a) (> c a))
         (+ b c))
        ((and (> a b) (> c b))
         (+ a c))
        (else (+ a b))))
 
;; 1.4 (if (> b 0) + -) 根据b的正负性返回函数+或-。
 
;; 1.5 函数p的定义 (define (p) (p)) 是不断递归调用自己,所以欲对其进行求值/展开会产生死循环。调用 (test 0 (p)) 时,若采用应用序求值,会先对函数的参数,包括 (p) 求值,因而产生死循环,无法输出结果;若采用正则序求值,则由于 (p) 的值事实上不会被展开,故会正常返回0。
 
;; 1.6 将在调用函数 new-if 时,应用序要求先算出其所有参数的值,new-if 的后两个参数——代表 if 的两个分支的代码都将被执行。所以 (sqrt-iter guess x) 必然需要调用 (sqrt-iter (improve guess x) x),这导致死循环。
 
;; 1.7
(define (sqrt x)
  (define (sqrt-iter guess)
    (define (good-enough?)
      (< (abs (/ (- (* guess guess) x) x)) 0.001))
    (define (improve)
      (define (average a b)
        (/ (+ a b) 2))
      (average guess (/ x guess)))
    (if (good-enough?)
        guess
        (sqrt-iter (improve))))
  (sqrt-iter 1.0))
 
;; 1.8
(define (cube-root x)
  (define (iter guess)
    (define (good-enough?)
      (< (abs (/ (- (* (* guess guess) guess) x) x)) 0.001))
    (define (improve)
      (/ (+ ( / x (* guess guess)) (* 2 guess)) 3))
    (if (good-enough?)
        guess
        (iter (improve))))
  (iter 1.0))
 
;; 1.9 第一个过程是递归的,表达式被递归地逐渐展开再合并;第二个过程中的递归是尾递归,不妨理解为迭代的。
 
;; 1.10 (f n)是n的2倍,(g n)是2的n次方,(h n)是2的(h (- n 1))次方。
 
;; 1.11
; 递归方式
(define (f n)
  (if (< n 3) n
      (+ (f (- n 1))
         (* 2 (f (- n 2)))
         (* 3 (f (- n 3))))))
 
; 迭代方式
 
(define (f n)
  (define (iter a b c i)
    (if (= i n) c
        (iter b c
              (+ (* 3 a) (* 2 b) c)
              (+ i 1))))
  (if (< n 3) n
      (iter 0 1 2 2)))
 
;; 1.12
(define (C n i)
  (if (or (= i 0) (= i n))
      1
      (+ (C (- n 1) (- i 1)) (C (- n 1) i))))
 
;; 1.13 可利用归纳法和定义直接证。
 
;; 1.14 空间复杂度为 O(amount),因为任何时刻调用链的长度(栈上的函数数量)不超过 amount+1;时间复杂度为 O(amount * result),因为每得到一种方案都调用一次 (cc 0 kinds-of-coins)。
 
;; 1.15 空间与时间复杂度均为O(log a)。
 
;; 1.16 prod*a^m是不变量。
(define (fast-expt b n)
  (define (iter prod a m)
    (if (= 0 m) prod
        (if (= 0 (remainder m 2))
            (iter prod (* a a) (/ m 2))
            (iter (* prod a) (* a a) (/ (- m 1) 2)))))
  (iter 1 b n))
 
;; 1.17
(define (fast-multi a b)
  (if (= 1 a) b
      (if (= 0 (remainder a 2))
          (double (fast-multi (halve a) b))
          (+ b (double (fast-multi (halve (- a 1)) b))))))
 
;; 1.18
(define (fast-multi a b)
  (define (iter sum i n)
    (if (= 0 n) sum
        (if (= 0 (remainder n 2))
            (iter sum (double i) (halve n))
            (iter (+ sum i) (double i) (halve (- n 1))))))
  (iter 0 a b))
 
;; 1.19 p’ = pp + qq,q’ = qq + 2pq。
 
;; 1.20 应用序需要4次;正则序需要更多,没仔细数,因为gcd函数体中出现了三处b,当b是调用remainder的形式时,它需要被展开若干次。
 
;; 1.21 略。
 
;; 1.22-24 我用的DrScheme没有定义runtime……555
 
;; 1.25 如果不在计算乘幂时随时取模,每次乘法将由于被乘数位数的迅速增长而不能被看做单位时间的原子操作。
 
;; 1.26 每次(expmod base exp m)当exp为正偶数时会调用两次而非一次(expmod base exp m),f(N)=2*f(N/2)+Θ(1)导致f(N)=Θ(N),而原始版本的f(N)=f(N/2)+Θ(1)导致f(N)=Θ(log N)。
 
;; 1.27 以下程序中,(find-carmichael n)找出不超过n的Carmichael数
 
(define (prime? n)
  (define (iter i)
    (cond
      ((> (square i) n) true)
      ((= 0 (remainder n i)) false)
      (else (iter (+ i 1)))))
  (iter 2))
 
(define (carmichael? n)
  (define (iter i)
    (cond
      ((= i n) true)
      ((not (= i (expmod i n n))) false)
      (else (iter (+ i 1)))))
  (and (not (prime? n)) (iter 2)))
 
(define (find-carmichael n)
  (define (iter i)
    (cond ((carmichael? i) (display i) (display "\n")))
    (cond ((< i n) (iter (+ i 1)))))
  (iter 2))
 
;; 1.28
(define (miller-rabin n)
  (define (exp-iter prod base exp)
    (cond ((and (< 1 prod) (< prod (- n 1)) (= (remainder (* prod prod) n) 1)) 0)
          ((= 0 exp) prod)
          (else (if (even? exp)
                    (exp-iter
                     prod
                     (remainder (* base base) n)
                     (/ exp 2))
                    (exp-iter
                     (remainder (* prod base) n)
                     (remainder (* base base) n)
                     (/ (- exp 1) 2))))))
  (define (check i)
    (= 1 (exp-iter 1 i (- n 1))))
  (define (check-iter i)
    (cond ((= i 0) true)
          (else (if (check (+ 2 (random (- n 3))))
                    (check-iter (- i 1)) false))))
  (or (= 2 n) (= 3 n)
      (and (< 2 n) (= 1 (remainder n 2)) (check-iter 20))))

Comments (5)

OIBH杯OI邀请赛8.04

今天一大早(也就是九点钟)起来发现OIBH论坛访问不太稳定,故发此贴。

本次比赛的时间是4月23日21:00至4月27日21:00,看题和提交的网址在这里。在看题前请备好 Foxit Reader。从你打开看题网页时开始计时,在10800秒内可以提交。

Comments (7)

给正为4月26日奋斗的学弟学妹们

大约是去年侥幸得了河南省选第一的缘故,好几个下一届的学弟学妹找我问怎样准备省选,在这边统一回答一下。

第一,把USACO Training做完。我曾经不无天真地认为,只要做完了USACO,进河南省队就没有任何问题。然而事实上,按照07年的情况来看,这个判断好像真的没有例外。在现在时间比较紧的情况下,写不出来就看题解没问题,但要保证看完题解以后代码是自己独立完成的,且你真的理解了这题目的算法。

第二,学一些最常用的高级数据结构与算法,但不必全学,按照自己的时间安排和兴趣选学好了,因为谁也不知道会考什么。参考清单:网络流(最好学Dinic算法,要知道最大流最小割定理的内容)、Treap或者Splay、KMP算法、Trie树、线段树、求割点和桥、收缩强联通分量。

第三,找一些高质量的套题限时做。这样的目的一方面是查漏补缺,另外是培养比赛的感觉。推荐山东、浙江、天津、重庆、河南的一些省选题,难度适中且比较贴近国情。也推荐USACO历次月赛的Gold和部分Silver题,这些题的好处是有完整的题解,当然阅读英文有障碍的就很遗憾了。

第四,把心态放低,告诉自己省选没有那么重要,进NOI并不是值得赌上全部去追求的东西,若选不上不一定是坏事,在河南这种太受歧视的地域,也许有保送资格以后把文化课搞好才是更合适的道路。把心态放稳,告诉自己河南是弱省,根本没什么强人,只要发挥正常就能进。心理调节很重要,我的经验是,当你心中不会经常出现“省选”或者“省队”这些字眼,而只会专注于一个一个的算法、一道一道的题目时,状态是最好的。

第五,需要更多心理调节以及任何合理的帮助可以直接打我手机,号码给我留个言就会告诉你,MM特别欢迎。

Comments (8)

OIBH队第一次参赛记

今天下午我们OIBH队参加了POJ的一场比赛,花了三个小时做出了四道题,结果是第41名,我们对这成绩还是相当满意的。做这场比赛的地点相当ws,是……紫云四舍(女生宿舍)。这还是我从小到大第一次进入女生宿舍的说。哦……队里有女生,在哪里做比赛这种事情就随她吧,她不介意我们都跑去她宿舍我们也就不如从命了。

(以下是比赛后的随记,没什么意义的。)

3月23日比赛的过程:迅速找到最水的题B,dd队长也迅速想好了一个错误的贪心算法,第一次由于VC6的不标准而CE,把CE改过来以后WA。这时候ww在想C,tt找到了另一道水题I。于是dd放弃B去写I,一次WA是由于题意有两种理解的可能,一开始猜错了,第二次就AC。这时ww给dd说了她推出的错误的DP方程,dd才刚刚写了一点,ww就说自己的方程有问题,又说J题比较水。于是dd开始自己看J题,结果dd唯一自己看的一道题还把输出要求看错了,一次WA。同时ww和tt经过仔细讨论以后确定了B的肯定对的思路,交给dd写一次,在第三次提交里AC。接着tt给dd讲了G的题意,dd认为可以用位运算降低常数来AC。但是由于dd对位运算的确不太熟,花了不少时间写出来的程序连样例都不过,dd也没信心再去改了。下面的时间都在讨论C题,在ww的错误的DP方程的启发下,dd想到了可能用博弈论里的Sprague-Grundy定理来解。花了一些时间回忆这个定理的准确内容后,dd迅速写好了SG函数的求值过程,交上去以后WA。认为可能是SG函数的递推的边界条件给的不对,对于最初的那几个情况的SG值的不同组合尝试了好几次以后仍然WA。这时ww和tt都比较闲,dd就说我给你们讲一讲什么是SG函数好了。他们差不多理解了以后,大家便一起猜边界条件的SG值到底应该是多少。这时我们发现了队里有一位女队员的优势:我们亲爱的女队员ww同学凭借女人的直觉准确的给出了边界条件的SG值应该是f[1]=f[2]=f[3]=1、f[4]=f[5]=2,当dd抱着再一次试试看的心情点了提交后,竟然出现了Accepted,全队成员都大叫了起来。AC4题已经达到了我们的预定目标,而且余下几题的确没有好写的,就结束了比赛的过程。

校赛的分工:dd队长负责写所有的程序,tt和ww负责读题、发现水题、给出正确思路,由dd负责实现,写出来以后如果没有百分的把握就让ww看一下再交,或者让tt造各种数据来测试。如果能顺利地把所有的水题AC掉,下面主要由AC人数来决定做哪些题。这一阶段就需要大家集思广益,通过头脑风暴式的讨论确定算法。这一阶段如果某人对讨论的内容不是太熟悉可以在一旁负责做数据。在做比较难的题的时候,不应该因罚时患得患失,只要手上的数据都能过就交。

Comments (7)

Emacs+GCC+GDB的最基本用法

用Emacs+GCC+GDB做OI题的最基本用法是很简单的。以下就是我在NOI期间的解决方案。

首先,用Emacs编辑好.cpp程序,例如test.cpp。

然后,M-x compile,把那一行命令改成g++ test.cpp -g -pg,这一行命令会被记住,再次编译同一个文件就不用改这一行了。参数“-g”是为了加入调试信息,“-pg”是为了profiling,若不需要可以去掉这些参数。如果有编译错误会在下面的buffer里显示的,直接用鼠标点就可以跳到相应的行。

如果要调试,直接M-x gdb

如果要运行,M-x shell,输入a.exe或者./a.out就能运行了。

稍微高级一点点的解决方案是在.emacs中加入一个自定义的编译过程,例如:


;;编译
(defun quick-compile ()
"A quick compile funciton for C++"
(interactive)
(compile (concat "g++ " (buffer-name (current-buffer)) " -g -pg"))
)
;;快捷键F9
(global-set-key [(f9)] 'quick-compile)

(注意由于本blog的bug以上代码中的引号有问题,请自行改成英文的单引号和双引号,也就是分号右边的那个键在英文输入法下的效果。)

这样就可以直接按f9编译了,也可以把gdb绑定到你喜欢的键上,同样是写一个global-set-key之类的。

顺便答一位读者问:

sr.png

Comments (4)

MinGW的GCC4.2.1还蛮好用的啊!

非常偶然的看到MinGW已经有了GCC4的Windows版,目前版本号是4.2.1(GNU的最新版本号是4.2.3,不过想来区别不大)。于是今天便下载试用了一下。经过非常初步的测试,GCC4的确比GCC3性能有所提高,体现在生成的可执行程序的大小和运行速度上。

安装步骤不能使用MinGW的安装程序(MinGW-5.1.3.exe),那个只能安装GCC3。MinGW GCC4还属于“Technology Preview”的阶段。

安装过程如下:

http://sourceforge.net/project/showfiles.php?group_id=2435去下载各组件的压缩包。下面所提到的压缩包的版本号截至2008.2.3都是最新的,当然若你看到了更高版本号的文件也可但用无妨。

必须的:

binutils-2.18.50-20080109.tar.gz
mingw-runtime-3.14.tar.gz
w32api-3.11.tar.gz
gcc-core-4.2.1-sjlj-2.tar.gz
gcc-g++-4.2.1-sjlj-2.tar.gz (编译C++程序必须,如果只是编译C程序不用)

可选的:

libgcc_sjlj_1.dll.gz (若提示缺少这个dll就下载吧)
mingw32-make-3.81-2.tar.gz (make程序,协调较复杂的编译过程,有些IDE的项目功能依赖它)
gdb-6.7.50.20071127-mingw.tar.bz2 (GDB程序,调试用的,有些IDE的调试功能依赖于它)

将所有压缩包解压到同一个文件夹,如C:/MinGW,如解压过程中提示有重复文件选日期更新的就可以了。

然后可以将bin目录下我们会用到的可执行文件改成更喜闻乐见的名称,如mingw32-g++-sjlj.exe和g++-sjlj.exe其实是同一个程序,任意保留一个并改名为g++.exe就行了。make、gdb等也可同样处理。

可以将包含g++等可执行程序的bin目录加入到系统path中,以方便使用。

配合ntemacs23(可在http://ntemacs.sf.net/下载)使用效果更佳。

Comments (7)

我为何中止DP系列文章的写作

前几天,在写作《动态规划中的线形模型》时,写到了讨论LIS问题的章节。顿悟一般,理解了LIS的数学本质。原来LIS问题就是这样一个问题:

一个有限集S,其中定义了两个全序关系<1和<2,用这两个全序关系定义一个偏序关系<:x<y当且仅当x<1y且x<2y。求偏序集(S,<)的最长链。

没错,这就是LIS问题的数学本质。知道了这个以后就可得出很多关于LIS及相关问题的结论。由于本篇文章并非教程,对这些结论不详加解释。

  1. 在原本的LIS问题中,第一个全序关系是“序列中x在y的前面”,第二个全序关系是“x小于y”。如果我们按照第二个全序关系对序列进行排序,可以得到原问题的一个对偶问题。即:对原序列进行排序,再把排序后序列的每个元素换成在原序列中的下标。两个对偶问题的答案是一样的,也就是说原序列中的LIS长度,肯定等于得到的新序列中的LIS长度。而对偶问题有一个非常好的性质:它的序列中的所有元素都在1..N中取值。这就带来了很多很多种O(NlogN)算法的思路,比如说可以用virtual binary tree。
  2. 既然LIS的本质是要求“偏序集的最长链”,那么就可以应用组合数学中有关的定理。例如熟悉的定理:最长链的长度等于反链的划分数;以及其对偶定 理:最长反链的长度等于链的划分数。所以LIS中那个让很多人不理解的结论就昭然若揭了。为何上升子序列的覆盖数等于最长不上升子序列的长度?因为上升子序列是链,不上升子序列是反链。
  3. 所有可以转化为LIS问题求解的OI题目一定满足LIS的数学本质。也就是说,需要用LIS的算法解决的题目,一定能找到两个全序关系,和由它们定义的偏序关系。例如那道经典的给定一些矩形造一座塔的问题,为什么按照矩形的长排序然后求宽的LIS。原因很简单,两个全序关系就是由矩形的长和宽定义的,而LIS就是要先按照其中一个全序关系排序以得到序列。
  4. 可以对LIS的数学本质进行扩展,也就是对LIS问题进行扩展,例如<1和<2不一定都得是全序关系,可以有一个是偏序关系,这个偏序关系也可能是另外两个全序关系定义的,等等……这样又可以解决更多题目,例如“三维导弹拦截”。
  5. ……

LIS问题其实只是线型动态规划中非常简单的一个模型,通过思考它的数学本质,竟然就可以得到如此多的结论,由这些结论就可以编制出无穷无尽的OI题……这让我欣喜异常。

然而,LIS的数学本质中涉及到的离散数学中的概念:全序、偏序、链,这都是我还非常非常陌生的概念。连这些概念都不敢说懂,我怎么敢说我懂了LIS问题?我怎么敢写出一份涉及它的教程?

是的,通过深入的思考,我充分理解到:动态规划的每一个模型,都可能有着深不见底的数学本质;不掌握足够多的数学工具,就不可能从本质上把握动态规划。

可我掌握的数学太少了,特别是用处特别大的离散数学,只是浅尝辄止。我深感现在的能力无法写出一份好的动态规划总结。更让我恐慌的是:竟然有那么多人,会那么仔细地,甚至带着崇拜的眼光,阅读我的总结。

由于数学能力不足,也为了避免误人子弟,我已经中止了DP系列文章的写作。今天发布的《背包问题九讲v1.1》是今年的最后一次发布。

我对期待着《动态规划中的线形模型》的所有人表示无比愧疚的歉意。你们高看了我,我目前的能力不足以从本质上把握诸多线形模型,更不足以写出好的总结。对不起,让你们失望了。

但是,要注意我说的是“中止”而非“终止”。那个雄心勃勃的计划没有夭折,只是暂时停顿了。在将来的某一天,在我自认为数学能力足够的时候,也许是在我加入某个ACM-ICPC队伍的时候,我会继续未完成的计划。动态规划的领域是如此迷人,哪怕需要再长的时间,我会将这个写作计划完成。

最后,再次向所有失望的人表示真诚的歉意。

Comments (6)

Graph-Theoretic Algorithms (Part I)

Chordal Graph:图中任何长度不小于4的环都存在弦(连接两个在环中不相邻顶点的边)。

Interval Graph:将图的每个顶点定义成一个区间,顶点间有边当且仅当两个区间相交。
Interval Graphs都是Chordal Graphs。

Perfect Elimination Order:无向图的顶点的一个排序,将这个作为拓扑序给边定向。满足每个点的前驱集合在原图中都是一个Clique。
图存在PEO当且仅当它是Chordal Graph。

当图存在PEO时,可以用贪心策略线性地解决几个在一般图中NP-Hard的问题。(Pred(v)表示PEO中在v点之前且与v点有边的顶点集合。)
(1) Coloring:按照PEO的顺序考察每个点,给它染没有被前驱用掉的最小颜色,也就是说:Color[i]=mex{Color[j]|j∈Pred(i)}。
(2) Clique:一定具有Pred(v)∪{v}的形式。
(3) Independent Set:按照PEO的逆序考察每个点,若当前它没有邻接点被选择,就选择它。

PEO的求法:
(1) Maximum Cardinality Search:每次找与当前序列中邻接的点数最多的点并加入序列中。O(V+E)
(2) lexBFS:不好实现,略。

对于给定的图和顶点序列,判断是否是PEO:对于序列中的每个点v,找它最靠后的那个前驱u,并判断u是否与v的所有其它前驱有边。O(V+E)
Chordal Graph Recognition:首先用MCS求出一个序列,再用上述方法判断它是不是PEO。

Simplicial Vertex: 邻接点的集合是一个Clique的顶点。
Chodal Graphs中肯定存在Simpicial Vertex(PEO的最后一个点)。所以PEO也可以通过不断找Simplicial Vertex并删除的方法求出(复杂度大)。事实上,只要Chodal Graphs不是完全图,至少存在两个Simplicial Vertices(PEO的最后一个点,以及不与最后一个点邻接的最靠后的点)。

Comparability Graph:存在满足无环(acyclic)和传递性(transitive)的边定向方式的无向图。其中传递性指:若存在边(u,v)和(v,w)则必存在边(u,w)。
Inverval Graph的补图是Comparability Graph(存在边的两个顶点的区间都是不相交的,按照区间的左右关系定向即可),逆命题不成立。Comparability Graphs与Chordal Graphs没有什么特别的关系。

Comparability Graph Recognition:暂略。

在Comparability Graph上可以定义每个顶点的Layer:L(v)=1+max(L(w)|w->v)。
L就是图的一种最优Coloring,同样选择L不同的顶点就是最大Clique。图的色数和团数都是最大的L值。
Minimum Clique Cover:流找出图的最小路径覆盖(用匹配),这就是Minimum Clique Cover了。这个值同样等于Independent Set的值。

Interval Graph Recognition:
(1) 一个图是Inverval Graph当且仅当它是Chordal Graph且它的补图是Comparability Graph。
(2) 一个图是Interval Graph当且仅当它的所有Maximal Clique可以被指定一个顺序,满足任何属于其中两个Clique的顶点也属于序列中处于这两个Clique中间的Clique。
算法是找出所有Maximal Clique并判断是否能如此排序。
用Chordal Graph的方法找出所有Maximal Clique。前述Chordal Graph的Clique形式Pred(v)∪{v}不是Clique当且仅当存在一个v的后继w,使v是w的最后一个前驱,且indeg(w)=indeg(v)+1。
然后要用PQ-Tree了,还不会呢……

Perfect Graph:满足团数等于色数且所有生成子图也满足的图。上面提到的一堆图都属于Perfect Graphs。
完美图的补图也是完美图。所以完美图(及其所有子图)满足:团数等于色数,独立数等于最小团覆盖数。这些数对于完美图都可以多项式地求出,但是我都不会求。

Intersection Graph:每个点代表一个集合,两个点有边当且仅当两个集合有公共元素。
所有图都是Intersection Graph,这是显然的。
一个图是Chordal Graph当且仅当它可以表示成一棵树的若干个子树的Intersection Graph。

Interval Graph的扩展:
(1) Circular-arc Graph就是将Interval Graph中的区间改成圆上的圆弧。
Circular-arc Graph不一定是Chordal Graph,也不一定是Perfect Graph。
(2) Boxicity Graph,将Interval Graph中的区间改成多维矩形。每个图都是Boxiicity Graph,对于适当的维d。对于Boxicity-2 Graphs,Clique是P的(算法?),但Independent Set依然NP-Hard。
(3) k-Interval Graph 每个点表示至多k个区间。没什么用的东西。

Comments (2)