SICP 习题 2.13 又像是一道数学证明题,和编程关系不大,不过这不能阻挡我们去完成它。
题目要求我们证明,当误差百分比很小的时候,可以使用一个简单的公式,根据被乘区间的误差去计算乘积的误差。
同时,为了简化问题,题目允许我们只计算所有数为正的情况,因为涉及到负数时,乘积的正负变化比较多样,不容易统一处理。
我看到题目后最直接的反应不是去证明它,而是通过程序去找到这个简单的公式,典型的程序员心理。
要做到这一点比较容易,我们构建一些误差百分比比较小的区间,将他们相乘,然后看看乘积的误差有什么规律。
我测试的代码如下:
(define first (make-center-percent 3647278 2)) (define second (make-center-percent 1223378 3))
(define mul-result (mul-interval first second))
(display (+ 0.0 (percent mul-result)))(newline)
为了防止巧合,可以多做几次以上的测试,结果比较明显,当我使用误差为2% 和 3% 区间相乘时,乘积区间的误差百分比大概是%5。
于是我们可以大胆的猜测这个简单公式就是将相乘区间的误差百分比相加。
进一步是数学证明,这一点麻烦一点,不感兴趣的同学们可以直接跳过了,不过如果你感兴趣的话,证明起来也不是太难。
题目允许我们只计算所有数为正数的情况,所以我们直接拿两个正区间来计算。
之前我们讨论过,如果两个区间都是正的,乘积区间的起点就是相乘区间起点的乘积,而乘积区间的终点就是相乘区间的终点的乘积。
假设我们又两个区间(a1 b1), (a2 b2)
有(a1 b 1) * ( a 2 b2) = ( a1* a2 b1 * b2)
区间1的误差百分比为 ((b1 - a1)/2) / ((b1 + a1)/2),
其实就是(b1 -a1) / (b1 +a1)
而区间2的误差百分比为(b2 - a2) / (b2 + a2)
区间1和区间2的误差百分比相加就是
(b1 -a1) / (b1 +a1) + (b2 - a2) / (b2 + a2)
等于:
((b1 - a1) * (b2 + a2) + (b2 - a2) * (b1 + a1) )/((b1 + a1) * (b2 + a2))
=> ( b1b2 - a1b2 + b1a2 - a1a2 + b1b2 - a2b1 + a1b2 - a2a1 )/ ((b1 + a1) * (b2 + a2))
=> (2* b1b2 - 2 a1a2)/ ((b1 + a1) * (b2 + a2))
=> (2* b1b2 - 2 a1a2)/ (b1b2 + a1b2 + b1a2 + a1a2)
=>2* (b1b2 - a1a2) / (b1b2 +a1a2 + a1b2 +b1a2)
因为区间1和区间2的误差百分比是很小的,我们可以认为(b1b2 +a1a2) 和 (a1b2 +b1a2)很接近,于是有
=> 2* (b1b2 - a1a2) / 2* (b1b2 +a1a2)
=> (b1b2 - a1a2) / (b1 b2 + a1a2)
而乘积区间的误差百分比就是:
(b1b2 - a1a2) / (b1 b2 + a1a2)
这就是习题2.13的数学证明,因为不是数学专业的,证明不是很严谨那种,大家大概明白什么意思就行,如果需要看严谨的证明,可以去google一番。