牛顿法求平方根

时间:2020-01-25 20:21:00

标签: scheme precision ieee-754

以下Scheme程序实现了Newton’s method,用于计算数字的平方根:

(import (scheme small))

(define (sqrt x)
  (define (sqrt-iter guess)
    (if (good-enough? guess)
      guess
      (sqrt-iter (improve guess))))
  (define (good-enough? guess)
    (define tolerance 0.001)
    (< (abs (- (square guess) x)) tolerance))
  (define (improve guess)
    (if (= guess 0) guess (average guess (/ x guess))))
  (define (average x y)
    (/ (+ x y) 2))
  (define initial-guess 1.0)
  (sqrt-iter initial-guess))

(display (sqrt 0)) (newline)
(display (sqrt 1e-12)) (newline)
(display (sqrt 1e-10)) (newline)
(display (sqrt 1e-8)) (newline)
(display (sqrt 1e-6)) (newline)
(display (sqrt 1e-4)) (newline)
(display (sqrt 1e-2)) (newline)
(display (sqrt 1e0)) (newline)
(display (sqrt 1e2)) (newline)
(display (sqrt 1e4)) (newline)
(display (sqrt 1e6)) (newline)
(display (sqrt 1e8)) (newline)
(display (sqrt 1e10)) (newline)
(display (sqrt 1e12)) (newline)
(display (sqrt 1e13))

输出:

0.03125
0.031250000010656254
0.03125000106562499
0.03125010656242753
0.031260655525445276
0.03230844833048122
0.10032578510960605
1.0
10.000000000139897
100.00000025490743
1000.0000000000118
10000.0
100000.0
1000000.0
[永远挂…]

如我们所见,这个幼稚的程序表现不佳:

  • 对于个小数字(在x = 1e-2以下),tolerance 0.001太大;
  • 对于个数字(从x = 1e13开始),该程序将永远挂起。

可以通过重新定义good-enough?过程来解决这两个问题:

(define (good-enough? guess)
  (= (improve guess) guess))

但是这种解决方案不是我发帖的目的。相反,我想了解为什么朴素的程序会以失败的方式失败,而不是大量失败。

我尚未阅读IEEE Standard for Floating-Point Arithmetic(IEEE 754),但据我了解,浮点数不能表示所有实数,并且具有绝对精度,对于小数字来说,该精度非常高而对于大数则非常低(此Wikipedia figure似乎证实了这一点)。换句话说,小的浮点数是密集的而大的浮点数是稀疏的。这样的结果是,如果guess尚未达到公差范围,并且如果(improve guess)无法再改善guess,那么天真程序将永远挂起,陷入无限递归中新的guess与旧的guess之间的距离低于旧的guess的绝对精度(因此新的guess与旧的浮点数相同guess,表示已达到(improve guess)的固定点。

为了确保对于给定的x,天真的程序返回,在我看来,该谓词必须成立:

tolerance> absolute_precision(sqrt(x))。

如果我们选择的tolerance为0.001 = 1e-3,则意味着sqrt(x)的绝对精度应小于1e-3。因此,根据上面的Wikipedia图中的binary64浮点数,sqrt(x)应该小于1e13,因此x应该小于1e26。

这是我的问题:

  1. 我的谓词正确吗?
  2. 为什么程序会从x = 1e13永久挂起,而不是预期的x = 1e26?
  3. 为什么程序会在{1e13,1e15,1e17,…,1e49}中与x一起永久挂起,而在大于1e49的任何x中却挂起,而在{1e14,中仍然以x返回, 1e16、1e18,...,1e48}?

注意。 —我在64位MacBook Pro上使用Chibi Scheme解释器,因此使用IEEE 754 binary64格式。

1 个答案:

答案 0 :(得分:1)

平方FlaUI时得到automation(二进制形式是无限的,因此与参数相比,精度降低)。如果将3162277.6601683795转换为十六进制,则会得到10000000000000.002,它是4x13个半字节,而10000000000000002使用2位。在float中,msb始终为1,因此被省略,因此我猜测使用了53位,并且跳过了许多小数,因为这是浮点的工作方式。您无法从任一侧容忍2386F26FC10002来接近2

我想您可以将猜测与之前的猜测进行比较,看看它是否在公差范围内。例如。

10000000000000

结果为0.001。与您的版本不同,当您减去猜测时,丢失的比特会使差异变小,因此即使容差为(define (sqrt x) (define (sqrt-iter guess prev-guess) (if (good-enough? guess prev-guess) guess (sqrt-iter (improve guess) guess))) (define (good-enough? guess prev-guess) (define tolerance 1e-20) (< (abs (- guess prev-guess)) tolerance)) (define (improve guess) (if (= guess 0) guess (average guess (/ x guess)))) (define (average x y) (/ (+ x y) 2)) (define initial-guess 1.0) (sqrt-iter initial-guess 0)) ,它也会产生3162277.6601683795的答案。

现在,如果您查看这些猜测,它将使跳跃幅度变得很大,从而使新的猜测始终更好,因此只有当该猜测接近实际结果时,它才会接近先前的猜测,我猜您会如果您的容忍度只是前一个的平方,则可以得到一个很好的估计。

我同意固定公差是不好的,我们应该有一些东西可以代替彼此猜测的猜测。当它们接近时,结果将始终接近1,而当偏离很大时,其结果将接近2或1/2。当1e-20会跳到标记的上方和下方而永不匹配时,仅进行无公差的比较可能会使您陷入无限循环,因此将公差放入其中是有原因的。

相关问题