오상우
오상우

Categories

  • lisp
  • scheme
  • sicp

연습문제 2.7 ~ 2.8

구간이라고 하는 데이터와 사칙연산 프로세스를 구현하는 문제이다.

구간 값의 뺄셈 sub-interval x y 는 x의 상한 - y의 하한을 상한으로, x의 하한 - y의 상한을 하한으로 하는 구간을 리턴한다.

; 연습문제 2.7
(define (make-interval x y) (cons x y))

(define (lower-bound i) (car i))

(define (upper-bound i) (cdr i))

; 집중과제
(define (add-interval x y)
  (make-interval (+ (lower-bound x) (lower-bound y))
                 (+ (upper-bound x) (upper-bound y))))

(define (mul-interval x y)
  (let ((p1 (* (lower-bound x) (lower-bound y)))
        (p2 (* (lower-bound x) (upper-bound y)))
        (p3 (* (upper-bound x) (lower-bound y)))
        (p4 (* (upper-bound x) (upper-bound y))))
    (make-interval (min p1 p2 p3 p4)
                   (max p1 p2 p3 p4))))

(define (div-interval x y)
  (if (or (= 0 (upper-bound y)) (= 0 (lower-bound y)))
      (display "Error: Division by 0!!") ; 연습문제 2.10
      (mul-interval x
                    (make-interval (/ 1.0 (upper-bound y))
                                   (/ 1.0 (lower-bound y))))))

; 테스트
(define interval1 (make-interval 5 50))
(define interval2 (make-interval 10 35))

(add-interval interval1 interval2)
(mul-interval interval1 interval2)
(div-interval interval1 interval2)

연습문제 2.9

구간 x 의 폭을 (x.u - x.l) / 2 라고 할 때,

두 구간 x, y 를 더해서 나온 구간의 폭은 ((x.u + y.u) - (x.l + y.l)) / 2 로 두 구간 x, y 각각의 폭의 합과 같다.
또한 두 구간 x, y 를 빼서 나온 구간의 폭은 ((x.u - y.l) - (x.l - y.u)) / 2 로 두 구간 x, y 각각의 폭의 합과 같다.

하지만 두 구간의 곱 연산은 구간 양 극단의 부호에 따라 어떤 값이 상/하한이 될지가 달라지기 때문에, 구간의 곱/나누기 연산의 폭은 위와 같은 방법으로 구할 수 없다.

연습문제 2.10

상/하한에 0이 포함된 구간으로 구간 나누기 연산을 할 경우 경고를 하는 프로시저를 위에 추가하였다.

연습문제 2.11 / 2.12

갑자기 클라이언트가 이게 아니라고 해서 코드를 새로 짜는 문제이다(…). 양 끝값이 아닌 중앙값과 오차범위를 퍼센트로 받아 구간으로 만든다.

; 연습문제 2.11

(define (make-center-width c w)
  (make-interval (- c w) (+ c w)))

(define (center i)
  (/ (+ (upper-bound i) (lower-bound i)) 2))

(define (width i)
  (/ (- (upper-bound i) (lower-bound i)) 2))

; 연습문제 2.12

(define (make-center-percent c p)
  (make-interval (- c (* c (/ p 100.0)))
                 (+ c (* c (/ p 100.0)))))

(define (percent i)
  (* 100 (/ (width i) (center i))))

연습문제 2.14

위에서 구현한 프로그램으로 대수적으로 동일한 두 프로시저를 계산하면 다른 값이 나오는데, 그 이유를 찾는 문제다.
대수적으로 동일한 두 식은 다음과 같다(병렬 저항 공식이라고 한다…).
(R1 * R2) / (R1 + R2)

1 / (1/R1 + 1/R2)

; 연습문제 2.14

(define (par1 r1 r2)
  (div-interval (mul-interval r1 r2)
                (add-interval r1 r2)))

(define (par2 r1 r2)
  (let ((one (make-interval 1 1)))
    (div-interval one
                  (add-interval (div-interval one r1)
                                (div-interval one r2)))))

(define cp1 (make-center-percent 5 1.1))
(define cp2 (make-center-percent 3 0.7))

(par1 cp1 cp2) ; (1.824065750371471 . 1.9272028016153455)
(par2 cp1 cp2) ; (1.8590554013124685 . 1.8909305349182761)
(div-interval cp1 cp1) ; (.9782393669634026 . 1.0222446916076844)

실제 두 값이 다름을 볼 수 있다. 아마 div-interval 연산에서 1.0을 나누는 부분에서 실수 나눗셈 연산이 잘못되었기 때문에 부정확한 수가 발생한 문제라고 생각된다.
그렇다면 par2 의 경우 나눗셈 연산이 1을 나누는 연산만 있고 두 수를 나누는 연산이 없기 때문에 더 나은 프로그램인걸까? 아직 잘 모르겠다…ㅠ

연습문제 2.16

아주 어렵다고 경고까지 붙어있는 문제.

즉 우리가 만든 데이터는 일반적인 수가 아닌데, 일반적인 수에 적용해야 하는 공식을 적용하려 하다 보니 생기는 문제라는 것이다.

예를 들어, 구간 a = (2, 8) 이 있다고 가정하자.
이때 a / a 는 몇인가? 일반적인 나눗셈에서 자기 자신을 자기 자신으로 나누면 1이 나와야 하지만, 실제로는 (0.25, 4) 라는 구간값이 나온다.