# 1.1.7示例：牛顿法求平方根

> 译者： [https://sicp.comp.nus.edu.sg/chapters/9](https://sicp.comp.nus.edu.sg/chapters/9)

如上所述，函数非常类似于普通的数学函数。 它们指定由一个或多个参数确定的值。 但是数学函数和计算机函数之间存在重要区别。 计算机功能必须有效。

例如，考虑计算平方根的问题。 我们可以将平方根函数定义为![%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Csqrt%7Bx%7D%20%3D%5Ctext%7B%20the%20%7Dy%5Ctext%7B%20such%20that%20%7Dy%20%5Cgeq%200%5Ctext%7B%20and%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%5E2%20%3D%20x%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20](img/88ee3e79a312d04eba5793e7c60281ba.jpg)。这描述了一个完全合法的数学函数。 我们可以使用它来识别一个数字是否是另一个数字的平方根，或者通常可以得出有关平方根的事实。 另一方面，该定义未描述计算机功能。 确实，它几乎没有告诉我们如何实际找到给定数字的平方根。 在伪JavaScript中重新定义此定义无济于事：

```js
function sqrt(x) {
    return the y with y >= 0 &&
                      square(y) === x;
}
```

This only begs the question.

数学功能和计算机功能之间的差异反映了描述事物的属性和描述如何做事情之间的一般区别，或者有时也称为陈述性知识和命令性知识之间的区别。 在数学中，我们通常与声明性（什么是）描述有关，而在计算机科学中，我们通常与命令性（如何做）描述有关。 [[1]](9#footnote-1)

一个如何计算平方根？ 最常见的方法是使用牛顿的逐次逼近方法，该方法表示，只要我们对数字![x](img/40779fc60a53ff2b70f832ec10cade09.jpg)的平方根的值有一个猜测![y](img/c592009395c2de830215c39f7bb6f97b.jpg)，我们就可以执行简单的操作以获得更好的猜测（ 通过将![y](img/c592009395c2de830215c39f7bb6f97b.jpg)与![x%2Fy](img/8db5e9c9e4e65ef361a6aa73433b599d.jpg)进行平均，可以更接近实际平方根）。 [[2]](9#footnote-2) 例如，我们可以如下计算2的平方根。 假设我们最初的猜测是1：

![](img/73ccf9997725cd88b8973a7dd82534e4.jpg)

Continuing this process, we obtain better and better approximations to the square root.

现在让我们根据功能对过程进行形式化。 我们先从“拉迪坎德”的值（我们试图计算其平方根的数字）开始，然后是“猜测”值。 如果猜测对我们的目的足够好，那么我们就完成了； 如果不是，我们必须以更好的猜测来重复该过程。 我们将此基本策略编写为函数：

```js
function sqrt_iter(guess, x) {
    return good_enough(guess, x)
           ? guess
           : sqrt_iter(improve(guess, x), x);
}
```

通过用radicand的商和旧的猜测取平均值来改善猜测：

```js
function improve(guess, x) {
    return average(guess, x / guess);
}
```

where

```js
function average(x,y) {
    return (x + y) / 2;
}
```

 <split>我们还必须说&lt;quote&gt;足够好。&lt;/quote&gt; 下面将进行说明，但这并不是一个很好的测试。 （请参阅练习 [1.7](9#ex_1.7) 。）其想法是改善答案，直到答案足够接近为止，以使其平方与弧度之差小于预定公差（此处为0.001）：

```js
function good_enough(guess, x) {
    return abs(square(guess) - x) < 0.001;
}
```</split>  <split>最后，我们需要一种入门方法。 例如，我们总是可以猜测任何数字的平方根为1：

```js
function sqrt(x) {
    return sqrt_iter(1, x);
}
```</split> 

如果将这些声明键入解释器，则可以使用`sqrt`，就像可以使用任何函数一样：

```js
sqrt(9);
```

```js
sqrt(100 + 37);
```

```js
sqrt(sqrt(2) + sqrt(3));
```

```js
square(sqrt(1000));
```

 <split>`sqrt`程序还说明，到目前为止，我们已经介绍的简单功能语言足以编写可以用C或Pascal编写的任何纯数字程序。 这似乎令人惊讶，因为我们尚未引入任何指导计算机反复执行某些操作的迭代（循环）结构。 另一方面，函数`sqrt_iter`演示了如何使用普通调用函数的功能而不使用特殊构造来完成迭代。 [[3]](9#footnote-3)</split> <exercise><split>Alyssa P. Hacker doesn't like the syntax of conditional expressions, involving the characters `?` and `:`. <quote>Why can't I just declare an ordinary conditional function whose application works just like conditional expressions?</quote> she asks. Alyssa's friend Eva Lu Ator claims this can indeed be done, and she declares a `conditional` function as follows:

```js
function conditional(predicate, then_clause, else_clause) {		    
    return predicate ? then_clause : else_clause;
}
```

Eva demonstrates the program for Alyssa:

```js
conditional(2 === 3, 0, 5);
```

evaluates as expected to 5, and

```js
conditional(1 === 1, 0, 5);
```

evaluates as expected to 0. Delighted, Alyssa uses `conditional` to rewrite the square-root program:

```js
function sqrt_iter(guess, x) {
    return conditional(good_enough(guess, x),
                       guess,
                       sqrt_iter(improve(guess, x),
                                 x));
}
```

What happens when Alyssa attempts to use this to compute square roots? Explain.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_9_1_div">Solution</button><solution>Any call of `sqrt_iter` leads immediately to an infinite loop. The reason for this is our applicative-order evaluation. The evaluation of the return expression of `sqrt_iter` needs to evaluate its arguments first, including the recursive call of `sqrt_iter`, regardless whether the predicate evaluates to _true_ or _false_. The same of course happens with the recursive call, and thus the function `conditional` never actually gets applied.</solution></split></exercise><exercise>The `good_enough` test used in computing square roots will not be very effective for finding the square roots of very small numbers. Also, in real computers, arithmetic operations are almost always performed with limited precision. This makes our test inadequate for very large numbers. Explain these statements, with examples showing how the test fails for small and large numbers. An alternative strategy for implementing `good_enough` is to watch how `guess` changes from one iteration to the next and to stop when the change is a very small fraction of the guess. Design a square-root function that uses this kind of end test. Does this work better for small and large numbers?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_9_2_div">Solution</button><solution>The absolute tolerance of 0.001 is too large when computing the square root of a small value. For example, `sqrt(0.0001)` results in 0.03230844833048122 instead of the expected value 0.01, an error of over 200%.On the other hand, for very large values, rounding errors might make the algorithm fail to ever get close enough to the square root, in which case it will not terminate terminates.The following program alleviates the problem by replacing an absolute tolerance with a relative tolerance.

```js
const error_threshold = 0.01;
function good_enough(guess,x) {
    return relative_error(guess, improve(guess, x))
           < error_threshold;
}
function relative_error(estimate, reference) {
    return abs(estimate - reference) / reference;
}
```</solution></exercise> <exercise>Newton's method for cube roots is based on the fact that if ![y](img/c592009395c2de830215c39f7bb6f97b.jpg) is an approximation to the cube root of ![x](img/40779fc60a53ff2b70f832ec10cade09.jpg), then a better approximation is given by the value ![%20%5Cfrac%7Bx%2Fy%5E%7B2%7D%2B2y%7D%20%7B3%7D%20](img/5943cdb04aea67c78edc46f76afe0c27.jpg) Use this formula to implement a cube-root function analogous to the square-root function. (In section <ref name="sec:proc-returned-values">[1.3.4](22)</ref> we will see how to implement Newton's method in general as an abstraction of these square-root and cube-root functions.)<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_9_3_div">Solution</button> <solution>```js
function good_enough(guess, x) {
    return abs(cube(guess) - x) < 0.001;
}
function div3(x, y) {
     return (x + y) / 3;
}
function improve(guess, x) {
    return div3(x / (guess * guess), 2 * guess);
}
function cube_root(guess, x) {
    return good_enough(guess, x)
               ? guess
               : cuberoot(improve(guess, x), x);
}
```</solution></exercise> 

* * *

[[1]](9#footnote-link-1) Declarative and imperative descriptions are intimately related, as indeed are mathematics and computer science. For instance, to say that the answer produced by a program is <quote>correct</quote> is to make a declarative statement about the program. There is a large amount of research aimed at establishing techniques for proving that programs are correct, and much of the technical difficulty of this subject has to do with negotiating the transition between imperative statements (from which programs are constructed) and declarative statements (which can be used to deduce things). In a related vein, an important current area in programming-language design is the exploration of so-called very high-level languages, in which one actually programs in terms of declarative statements. The idea is to make interpreters sophisticated enough so that, given <quote>what is</quote> knowledge specified by the programmer, they can generate <quote>how to</quote> knowledge automatically. This cannot be done in general, but there are important areas where progress has been made. We shall revisit this idea in chapter 4.

[[2]](9#footnote-link-2) This square-root algorithm is actually a special case of Newton's method, which is a general technique for finding roots of equations. The square-root algorithm itself was developed by Heron of Alexandria in the first century <sc>a.d</sc>. We will see how to express the general Newton's method as a JavaScript function in section <ref name="sec:proc-returned-values">[1.3.4](22)</ref>.

[[3]](9#footnote-link-3) Readers who are worried about the efficiency issues involved in using function calls to implement iteration should note the remarks on <quote>tail recursion</quote> in section <ref name="sec:recursion-and-iteration">[1.2.1](12)</ref>.

