练习 1.31

这题有几个子问题,我们逐个来解决。

product(递归版本)

product 计算给定范围中各点的某个函数值的乘积,它的递归版本和书本 38 页的递归版本 sum 非常相似:

;;; 31-rec-product.scm

(define (product term a next b)
    (if (> a b)
        1
        (* (term a)
           (product term (next a) next b))))

测试:

1 ]=> (load "31-rec-product.scm")

;Loading "31-rec-product.scm"... done
;Value: product

1 ]=> (product (lambda (x) x)
               1
               (lambda (i) (+ i 1))
               10)

;Value: 3628800

product(迭代版本)

迭代版本的 product 也和 练习 1.30 的迭代版本 sum 非常相似:

;;; 31-iter-product.scm

(define (product term a next b)
    (define (iter a result)
        (if (> a b)
            result
            (iter (+ a 1)
                  (* (term a) result))))
    (iter a 1))

测试:

1 ]=> (load "31-iter-product.scm")

;Loading "31-iter-product.scm"... done
;Value: product

1 ]=> (product (lambda (x) x)
               1
               (lambda (i) (+ i 1))
               10)

;Value: 3628800

使用 product 定义 factorial

factorial 也就是阶乘,书本 21 页介绍过阶乘的概念。

以下是使用 product 重定义的 factorial

;;; 31-factorial.scm

(load "31-iter-product.scm")

(define (factorial n)
    (product (lambda (x) x)
             1
             (lambda (i) (+ i 1))
             n))

测试:

1 ]=> (load "31-factorial.scm")

;Loading "31-factorial.scm"...
;  Loading "31-iter-product.scm"... done
;... done
;Value: factorial

1 ]=> (factorial 10)

;Value: 3628800

计算 pi 的近似值

可以将题目给出的公式

\[\frac{\pi}{4} = \frac{ 2 \cdot 4 \cdot 4 \cdot 6 \cdot 6 \cdot 8 \dotsi}{ 3 \cdot 3 \cdot 5 \cdot 5 \cdot 7 \cdot 7 \dotsi}\]

转换成

\[\pi = 4 \cdot \left(\frac{2 \cdot 4 \cdot 4 \cdot 6 \cdot 6 \cdot 8 \dotsi}{3 \cdot 3 \cdot 5 \cdot 5 \cdot 7 \cdot 7 \dotsi}\right)\]

很明显,公式括号里面的分子和分母,可以分别用两个乘法序列来表示。

分子的乘法序列 \(2, 4, 4, 6, 6, 8, \dotsi\) 可以用函数 numer-term 生成:

;;; 31-numer-term.scm

(define (numer-term i)
    (cond ((= i 1)
            2)
          ((even? i)
            (+ i 2))
          (else
            (+ i 1))))

分母的乘法序列 \(3, 3, 5, 5, 7, 7, \dotsi\) 可以用函数 denom-term 生成:

;;; 31-denom-term.scm

(define (denom-term i)
    (if (odd? i)
        (+ i 2)
        (+ i 1)))

组合起 productnumer-termdenom-term ,完整的 \(\pi\) 值计算程序的定义如下:

;;; 31-pi.scm

(load "31-iter-product.scm")
(load "31-numer-term.scm")
(load "31-denom-term.scm")

(define (pi n)
    (* 4
        (exact->inexact
            (/ (product numer-term
                        1
                        (lambda (i) (+ i 1))
                        n)
               (product denom-term 
                        1
                        (lambda (i) (+ i 1))
                        n)))))

程序使用了 exact->inexact 函数转换除法的商,确保计算所得的结果为浮点数格式(而不是份数格式)。

最后,对程序进行测试:

1 ]=> (load "31-pi.scm")

;Loading "31-pi.scm"...
;  Loading "31-iter-product.scm"... done
;  Loading "31-numer-term.scm"... done
;  Loading "31-denom-term.scm"... done
;... done
;Value: pi

1 ]=> (pi 1)

;Value: 2.6666666666666665

1 ]=> (pi 10)

;Value: 3.2751010413348074

1 ]=> (pi 100)

;Value: 3.1570301764551676

1 ]=> (pi 1000)

;Value: 3.1431607055322663

1 ]=> (pi 10000)

;Value: 3.1417497057380523

1 ]=> (pi 100000)

;Value: 3.1416083612781764

增大和减少输入参数 n 可以控制计算出的 \(\pi\) 值的精度。

讨论

blog comments powered by Disqus