# 1.2.5最大公约数

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

将两个整数![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)和![b](img/6872867a863714d15d9a0d64c20734ce.jpg)的最大公约数（GCD）定义为最大整数，该整数将![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)和![b](img/6872867a863714d15d9a0d64c20734ce.jpg)均除而无余数。 例如，16和28的GCD为4.在第2章中，当我们研究如何实现有理数算法时，我们将需要能够计算GCD以便将有理数减少到最低限度。 （要将有理数减少到最低的项，我们必须将分子和分母除以它们的GCD。例如，将16/28减为4/7。）找到两个整数的GCD的一种方法是将它们乘以因子，然后求和。 搜索常见因素，但是有一种著名的算法效率更高。

该算法的思想基于以下观察：如果![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)除以![b](img/6872867a863714d15d9a0d64c20734ce.jpg)时余数为![r](img/f9f040e861365a0560b2552b4e4e17da.jpg)，则![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)和![b](img/6872867a863714d15d9a0d64c20734ce.jpg)的公因数与公因数完全相同 ![b](img/6872867a863714d15d9a0d64c20734ce.jpg)和![r](img/f9f040e861365a0560b2552b4e4e17da.jpg)的定义。 因此，我们可以使用等式![%0A%20%20%20%20%20%20%20%20%20%20%5Ctextrm%7BGCD%7D%20%28a%2C%20b%29%20%3D%20%5Ctextrm%7BGCD%7D%28b%2C%20r%29%0A%20%20%20%20%20%20%20%20%20%20](img/e92a57546c9539df9ebd9d2f2cc04b92.jpg)来将计算GCD的问题逐步减少到计算越来越小的整数对的GCD的问题。 例如，\ begin {eqnarray *} \ textrm {GCD}（206,40）& = & \ textrm {GCD}（40,6）\\ & = & \ textrm {GCD} （6,4）\\ & = & \ textrm {GCD}（4,2）\\ & = & \ textrm {GCD}（2,0）\\ & = & 2 \ end {eqnarray *}将![%5Ctextrm%7BGCD%7D%28206%2C%2040%29](img/f84558e1e0fc08e17bfd2d82bbb1015e.jpg)减小为![%5Ctextrm%7BGCD%7D%282%2C%200%29](img/54d69a355343268eacdf8492a872635f.jpg)，即2。可能显示从任何两个正整数开始并进行重复的归约总是会最终产生一个对，其中第二个数字为0 然后，GCD是该对中的另一个数字。 这种用于计算GCD的方法称为 _Euclid算法_。 [ [1]](16#footnote-1)

将欧几里得算法很容易表达为一个函数：

```js
function gcd(a, b) {
    return b === 0 ? a : gcd(b, a % b);
}
```

这将生成一个迭代过程，其步数随着所涉及数字的对数而增加。

Euclid算法所需的步数具有对数增长的事实与斐波那契数具有有趣的关系：

**Lamé定理：**如果欧几里得算法要求![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg)步骤来计算某对的GCD，则该对中较小的数字必须大于或等于第![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg)个斐波那契数。 [ [2]](16#footnote-2)

我们可以使用该定理来获得Euclid算法的增长阶估计。 令![n](img/493731e423d5db62086d0b8705dda0c8.jpg)为该函数的两个输入中的较小者。 如果该过程采取![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg)步骤，那么我们必须具有![n%5Cgeq%20%7B%5Ctextrm%7BFib%7D%7D%20%28k%29%5Capprox%5Cphi%5Ek%2F%5Csqrt%7B5%7D](img/1953f6e1ed24ea145326746e44ea055e.jpg)。 因此，步数![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg)随着![n](img/493731e423d5db62086d0b8705dda0c8.jpg)的对数增长（以![%5Cphi](img/b4591b72892f13da37e97ae1e729cd99.jpg)为底）。 因此，生长顺序为![%5CTheta%28%5Clog%20n%29](img/b3291b1e09d9706bcea64c5e1f45d456.jpg)。

<exercise>The process that a function generates is of course dependent on the rules used by the interpreter. As an example, consider the iterative `gcd` function given above. Suppose we were to interpret this function using normal-order evaluation, as discussed in section <ref name="sec:substitution-model">[1.1.5](7)</ref>. (The normal-order-evaluation rule for `if` is described in exercise <ref name="ex:normal-order-vs-appl-order-test">[1.5](8#ex_1.5)</ref>.) Using the substitution method (for normal order), illustrate the process generated in evaluating `gcd(206, 40)` and indicate the remainder operations that are actually performed. How many remainder operations are actually performed in the normal-order evaluation of `gcd(206, 40)`? In the applicative-order evaluation?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_16_1_div">Solution</button> <solution><split>1.  使用正常顺序评估，该过程将进行18次余数运算。 14在评估条件的同时，在最终还原阶段休息。

    ```js
    gcd(206, 40)
    40 === 0 ? 206 : gcd(40, 206 % 40)
    gcd(40, 206 % 40)
    206 % 40 === 0 ? 40 : gcd(206 % 40,
                              40 % (206 % 40))
    // remainder operation (1)
    6 === 0 ? 40 : gcd(206 % 40,
                       40 % (206 % 40))
    gcd(206 % 40, 40 % (206 % 40))
    40 % (206 % 40) === 0 
        ? 206 % 40 
        : gcd(40 % (206 % 40),
              (206 % 40) % (40 % (206 % 40)))
    // remainder operations (2) and (3)
    4 === 0 
        ? 206 % 40 
        : gcd(40 % (206 % 40),
              (206 % 40) % (40 % (206 % 40)))
    gcd(40 % (206 % 40), (206 % 40) % (40 % (206 % 40)))
    (206 % 40) % (40 % (206 % 40)) === 0 
        ? 40 % (206 % 40)
        : gcd((206 % 40) % (40 % (206 % 40)), 
              (40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40)))
    // remainder operations (4), (5), (6), (7)
    2 === 0
        ? 40 % (206 % 40)
        : gcd((206 % 40) % (40 % (206 % 40)), 
              (40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40))))
    gcd((206 % 40) % (40 % (206 % 40)), 
        (40 % (206 % 40)) % ((206 % 40) % (40 % (206 % 40)))
    (40 % (206 % 40)) % ((206 % 40) % (40 % (206 % 40))) === 0
        ? (206 % 40) % (40 % (206 % 40)) 
        : gcd((40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40)),
              ((206 % 40) % (40 % (206 % 40))) %
              ((40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40))))
    // remainder operations 
                        (8), (9), (10), (11), (12), (13), (14)
    0 === 0
        ? (206 % 40) % (40 % (206 % 40)) 
        : gcd((40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40)),
              ((206 % 40) % (40 % (206 % 40))) %
              ((40 % (206 % 40)) % ((206 % 40) % (40 % 
                                                 (206 % 40))))
    (206 % 40) % (40 % (206 % 40))    
    // remainder operations (15), (16), (17), (18)
    2
    ```

2.  使用应用程序订单评估，该过程执行4个剩余操作。

    ```js
    gcd(206, 40)
    40 === 0 ? 206 : gcd(40, 206 % 40)
    gcd(40, 206 % 40)
    // remainder operation (1)
    gcd(40, 6)
    6 === 0 ? 40 : gcd(6, 40 % 6)
    gcd(6, 40 % 6)
    // remainder operation (2)
    gcd(6, 4)
    4 === 0 ? 6 : gcd(4, 6 % 4)
    gcd(4, 6 % 4)
    // remainder operation (3)
    gcd(4, 2)
    2 === 0 ? 4 : gcd(2, 4 % 2)
    gcd(2, 4 % 2)
    // remainder operation (4)
    gcd(2, 0)
    0 === 0 ? 2 : gcd(0, 2 % 0)
    2
    ```</split></solution></exercise> 

* * *

[[1]](16#footnote-link-1) Euclid's Algorithm is so called because it appears in Euclid's _Elements_ (Book 7, ca. 300 <sc>b.c</sc>. According to Knuth (1973), it can be considered the oldest known nontrivial algorithm. The ancient Egyptian method of multiplication (exercise <ref name="ex:it-pro-mult-int">[1.18](15#ex_1.18)</ref>) is surely older, but, as Knuth explains, Euclid's algorithm is the oldest known to have been presented as a general algorithm, rather than as a set of illustrative examples.

[[2]](16#footnote-link-2) This theorem was proved in 1845 by Gabriel Lamé, a French mathematician and engineer known chiefly for his contributions to mathematical physics. To prove the theorem, we consider pairs ![%28a_k%20%2Cb_k%29](img/a54139c294df649476a45dbfd34e82e9.jpg), where ![a_k%5Cgeq%20b_k](img/21b802fa00c9c29360e964b574bb7d78.jpg), for which Euclid's Algorithm terminates in ![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg) steps. The proof is based on the claim that, if ![%28a_%7Bk%2B1%7D%2C%5C%20b_%7Bk%2B1%7D%29%20%5Crightarrow%20%28a_%7Bk%7D%2C%5C%20b_%7Bk%7D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Crightarrow%20%28a_%7Bk-1%7D%2C%5C%20b_%7Bk-1%7D%29](img/fe96b8dd71f5cc434608dd6e68818d42.jpg) are three successive pairs in the reduction process, then we must have ![b_%7Bk%2B1%7D%5Cgeq%20b_%7Bk%7D%20%2B%20b_%7Bk-1%7D](img/e27aeb3c426b4eb920eeeaa7ee4c7fb5.jpg). To verify the claim, consider that a reduction step is defined by applying the transformation ![a_%7Bk-1%7D%20%3D%20b_%7Bk%7D](img/071aa4a1c8f03a963191729f9966703f.jpg), ![b_%7Bk-1%7D%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Ctext%7B%20remainder%20of%20%7D%20a_%7Bk%7D%5Ctext%7B%20divided%20by%20%7Db_%7Bk%7D](img/3d00bb6330a9ca3a7581a3c1e5c575cc.jpg). The second equation means that ![a_%7Bk%7D%20%3D%20qb_%7Bk%7D%20%2B%20b_%7Bk-1%7D](img/1c16cccb1963b806f4372ba87f33c1f6.jpg) for some positive integer ![q](img/807ceeb0b2cebef0db0b731ff4d59f51.jpg). And since ![q](img/807ceeb0b2cebef0db0b731ff4d59f51.jpg) must be at least 1 we have ![a_%7Bk%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20qb_%7Bk%7D%20%2B%20b_%7Bk-1%7D%20%5Cgeq%20b_%7Bk%7D%20%2B%20b_%7Bk-1%7D](img/79f554cff90a0122135c34cfe4dd5eb5.jpg). But in the previous reduction step we have ![b_%7Bk%2B1%7D%3D%20a_%7Bk%7D](img/dc93c50552fd54c4a2ee08fd852b2c9c.jpg). Therefore, ![b_%7Bk%2B1%7D%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20a_%7Bk%7D%5Cgeq%20b_%7Bk%7D%20%2B%20b_%7Bk-1%7D](img/1d61641a898d40c9ba83ce56a9b162fd.jpg). This verifies the claim. Now we can prove the theorem by induction on ![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg), the number of steps that the algorithm requires to terminate. The result is true for ![k%3D1](img/6157e00cb221eeddd3d4f4772b0d1fc6.jpg), since this merely requires that ![b](img/6872867a863714d15d9a0d64c20734ce.jpg) be at least as large as ![%5Ctext%7BFib%7D%281%29%3D1](img/53d3ce0f61e1908e58152e4e8a7f9742.jpg). Now, assume that the result is true for all integers less than or equal to ![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg) and establish the result for ![k%2B1](img/0b0735b3fceb2c2324b9893f388a5488.jpg). Let ![%28a_%7Bk%2B1%7D%2C%5C%20b_%7Bk%2B1%7D%29%5Crightarrow%28a_%7Bk%7D%2C%5C%20b_%7Bk%7D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Crightarrow%28a_%7Bk-1%7D%2C%5C%20b_%7Bk-1%7D%29](img/9f7d2084836b87746c858fce9e6d488d.jpg) be successive pairs in the reduction process. By our induction hypotheses, we have ![b_%7Bk-1%7D%5Cgeq%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%5Ctextrm%7BFib%7D%7D%28k-1%29](img/620e40316a8b3b6732d2449a88bb6792.jpg) and ![b_%7Bk%7D%5Cgeq%20%7B%5Ctextrm%7BFib%7D%7D%28k%29](img/9d47f06924f5783bf9b93f3486426efb.jpg). Thus, applying the claim we just proved together with the definition of the Fibonacci numbers gives ![b_%7Bk%2B1%7D%20%5Cgeq%20b_%7Bk%7D%20%2B%20b_%7Bk-1%7D%5Cgeq%20%7B%5Ctextrm%7BFib%7D%7D%28k%29%20%2B%20%7B%5Ctextrm%7BFib%7D%7D%28k-1%29%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%7B%5Ctextrm%7BFib%7D%7D%28k%2B1%29](img/09907bc5ff8c86bea79dfb2911292cf7.jpg), which completes the proof of Lamé's Theorem.

