# 1.2.3增长顺序

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

前面的示例说明，进程在消耗计算资源的速率上可能有很大差异。 描述这种差异的一种方便的方法是使用_增长顺序_的概念来获得随着输入变大而过程所需的资源的总体度量。

令![n](img/493731e423d5db62086d0b8705dda0c8.jpg)为衡量问题大小的参数，令![R](img/502cdd9c79852b33d2a6d18ba5ec3102.jpg)（![n](img/493731e423d5db62086d0b8705dda0c8.jpg)）为过程为问题![n](img/493731e423d5db62086d0b8705dda0c8.jpg)所需的资源量。 在我们之前的示例中，我们将![n](img/493731e423d5db62086d0b8705dda0c8.jpg)用作要为其计算给定函数的数字，但是还有其他可能性。 例如，如果我们的目标是计算数字的平方根的近似值，则可以将![n](img/493731e423d5db62086d0b8705dda0c8.jpg)设为所需的数字精度。 对于矩阵乘法，我们可以将![n](img/493731e423d5db62086d0b8705dda0c8.jpg)用作矩阵中的行数。 通常，问题的许多性质都需要分析给定的过程。 同样，![R](img/502cdd9c79852b33d2a6d18ba5ec3102.jpg)（![n](img/493731e423d5db62086d0b8705dda0c8.jpg)）可能会测量使用的内部存储寄存器的数量，执行的基本机器操作的数量等等。 在一次仅执行固定数量操作的计算机中，所需时间将与执行的基本计算机操作数量成比例。

我们说，如果存在独立于![n](img/493731e423d5db62086d0b8705dda0c8.jpg)的正常数![k_1](img/fbcafed3c522716470133adf771f0fab.jpg)和![k_2](img/482494d27ca34bd16b71645d2ebc2d88.jpg)，则![R%28n%29](img/01782f2e908b5bddc0668561d6a25a18.jpg)的增长顺序为![%5CTheta%28f%28n%29%29](img/d5c1ebd1f0820ddd7c757351445e290d.jpg)，写为![R%28n%29%3D%5CTheta%28f%28n%29%29](img/189044d3ab06c4e52b1a812825a0371c.jpg)（发音为![f%28n%29](img/55c1a6f95b884f5582db12270ef5a43e.jpg) 的 &lt;quote&gt;theta）。 这样![k_1f%28n%29%20%5Cleq%20R%28n%29%20%5Cleq%20k_2f%28n%29](img/12f607a4e4aa26341ae7859eff3e102f.jpg)对于![n](img/493731e423d5db62086d0b8705dda0c8.jpg)的任何足够大的值。 （换句话说，对于大的![n](img/493731e423d5db62086d0b8705dda0c8.jpg)，值![R%28n%29](img/01782f2e908b5bddc0668561d6a25a18.jpg)夹在![k_1f%28n%29](img/d290983ab252f1a5530867689eb2d087.jpg)和![k_2f%28n%29](img/54f1fd2fcc5739a55c82876b7143ac59.jpg)之间。）&lt;/quote&gt;

例如，使用 [1.2.1](12)[1.2.2](13) 节中描述的黄金分割率[ 。

增长顺序仅提供对流程行为的粗略描述。 例如，需要![n%5E2](img/78af23a97b83464655efc741bf99f6c7.jpg)个步骤的过程和需要![1000n%5E2](img/a3af3356c5331859f273fb66114ca644.jpg)个步骤的过程以及需要![3n%5E2%2B10n%2B17](img/c3ecbc7f52e77bb9ea6776595a0ac671.jpg)个步骤的过程都具有![%5CTheta%28n%5E2%29](img/e86789a30174e5928ea8d6cd8afb2845.jpg)个增长顺序。 另一方面，增长顺序提供了一个有用的指示，表明我们可以预期随着行为问题大小的变化，流程的行为也会发生变化。 对于![%5CTheta%28n%29](img/592c476de27cabe6879d3a7c537da592.jpg)（线性）过程，将大小增加一倍将使使用的资源量大约增加一倍。 对于指数过程，问题大小的每次增加都会使资源利用率乘以一个恒定因子。 在 [ 1.2 ](11) 的其余部分中，我们将研究两种算法，其增长顺序是对数的，因此问题大小加倍会使资源需求增加一个恒定量。

<exercise>Draw the tree illustrating the process generated by the `count_change` function of section <ref name="sec:tree-recursion">[1.2.2](13)</ref> in making change for 11 cents. What are the orders of growth of the space and number of steps used by this process as the amount to be changed increases?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_14_1_div">Solution</button><solution>The tree-recursive process generated in computing `cc(11, 5)` is illustrated by the image below, due to Toby Thain, assuming that the coin values in `first_denomination` are ![%5Cmathbb%7BC%7D_%7B1%7D%20%3D%201](img/769397df27496aaf02609d3894f6b178.jpg), ![%5Cmathbb%7BC%7D_%7B2%7D%20%3D%205](img/855f80d9a6f9ed0502e5caf603e6ce70.jpg), ![%5Cmathbb%7BC%7D_%7B3%7D%20%3D%2010](img/0b807adac335ab3af3a5f61dd9666dd2.jpg), ![%5Cmathbb%7BC%7D_%7B4%7D%20%3D%2025](img/7e329d9a1b5ad130b89dcb0451738b84.jpg) and ![%5Cmathbb%7BC%7D_%7B5%7D%20%3D%2050](img/8096d11767455d495290dc75964f7762.jpg).

![](img_javascript/ex1-14.png)

Let us consider the process for evaluating `cc(n, k)`, which means the amount to be changed is `n` and the number of kinds of coins is `k`. Let us assume the coin values are constants, not dependent on `n` or `k`.The space required for a tree-recursive process is—as discussed in section <ref name="sec:tree-recursion">[1.2.2](13)</ref>—proportional to the maximum depth of the tree. At each step from a parent to a child in the tree, either `n` strictly decreases (by a constant coin value) or `k` decreases (by 1), and leaf nodes have an amount of at most 0 or a number of kinds of coins of 0\. Thus, every path has a length of ![%5CTheta%28n%20%2B%20k%29](img/9fc31c1f583a17fefe1b2ce340a2f6bf.jpg), which is also the order of growth of the space required for `cc(n, k)`.Let us derive a function ![T%28n%2C%20k%29](img/cef96698c54b6b2ea756a5b240c01248.jpg) such that the time required for calculating `cc(n, k)` has an order of growth of ![%5CTheta%28T%28n%2C%20k%29%29](img/94e9e6a78fe1fae55c51b621ddc73b5c.jpg). The following argument is due to Yati Sagade, including the illustrations (<citation>Sagade 2015</citation>). Let us start with the call tree for changing some amount ![n](img/493731e423d5db62086d0b8705dda0c8.jpg) with just 1 kind of coin, i.e., the call tree for `cc(n, 1)`.

![](img_javascript/cc_1.png)

We are only allowed here to use one kind of coin, with value ![%5Cmathbb%7BC%7D_%7B1%7D%20%3D%201](img/769397df27496aaf02609d3894f6b178.jpg). The red nodes are terminal nodes that yield 0, the green node is a terminal node that yields 1 (corresponding to the first condition in the declaration of `cc`). Each nonterminal node spawns two calls to `cc`, one (on the left) with the same amount, but fewer kinds of coins, and the other (on the right) with the amount reduced by 1 and equal kinds of coins.Excluding the root, each level has exactly 2 nodes, and there are ![n](img/493731e423d5db62086d0b8705dda0c8.jpg) such levels. This means, the number of `cc` calls generated by a single `cc(n, 1)` call (including the original call) is: ![%0AT%28n%2C1%29%20%3D%202n%20%2B%201%20%3D%20%5CTheta%28n%29%0A](img/5df234500d4e582d88f39c96eb7e422d.jpg) Next, we will look at the call tree of `cc(n, 2)` to calculate ![T%28n%2C2%29](img/a6b5dff18c3e9135f0d4d64f0374afc2.jpg):

![](img_javascript/cc_2.png)

Here, we are allowed to use two denominations of coins: ![%5Cmathbb%7BC%7D_%7B2%7D%20%3D%205](img/855f80d9a6f9ed0502e5caf603e6ce70.jpg) and ![%5Cmathbb%7BC%7D_%7B1%7D%20%3D%201](img/769397df27496aaf02609d3894f6b178.jpg).Each black node spawns a `cc(m, 1)` subtree (blue), which we’ve already analyzed, and a `cc(m - 5, 2)` subtree. The node colored in red and green is a terminal node, but yields 0 if the amount is less than zero and 1 if the amount is exactly zero. Sagade denotes this final amount as ![%5Cepsilon](img/aeb302325ebc29add21f094ad38ad261.jpg), which can be ![%5Cle0](img/41597a7ebf95b8f224d0d40decd7441f.jpg).Excluding the root and and the last level in this tree which contains the red-green terminal node, there will be exactly ![%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B5%7D%7D%20%5Crfloor](img/05017c7a5a46baece41816f525ffa9e6.jpg) levels. Now each of these levels contains a call to `cc(m, 1)` (the blue nodes), each of which, in turn, is ![%5CTheta%28n%29](img/592c476de27cabe6879d3a7c537da592.jpg) in time. So each of these levels contains ![T%28n%2C1%29%20%2B%201](img/b8d07a51d84cb676a41b5daf7bbdf70b.jpg) calls to `cc`. Therefore, the total number of nodes (including the terminal node and the root) in the call tree for `cc(n, 2)` is: ![%0AT%28n%2C2%29%20%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B5%7D%20%7D%20%5Crfloor%20%28%20T%28n%2C1%29%20%2B%201%29%20%2B%202%20%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B5%7D%20%7D%20%5Crfloor%20%28%202n%20%2B%202%20%29%20%2B%202%20%3D%20%5CTheta%28n%5E2%29%0A](img/39f6e11fd18e49cc05046ccce7ed6159.jpg) Moving ahead, let’s take a look at the call tree of `cc(n, 3)`, i.e., we are now allowed to use three denominations of coins, the new addition being ![%5Cmathbb%7BC%7D_%7B3%7D%20%3D%2010](img/0b807adac335ab3af3a5f61dd9666dd2.jpg):

![](img_javascript/cc_3.png)

Here also, we see, similar to the previous case, that the total number of calls to `cc` will be ![%0AT%28n%2C3%29%20%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B10%7D%20%7D%20%5Crfloor%20%28%20T%28n%2C2%29%20%2B%201%29%20%2B%202%20%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B10%7D%20%7D%20%5Crfloor%20%5Ctimes%20%5CTheta%28n%5E2%29%20%2B%202%20%3D%20%5CTheta%28n%5E3%29%0A](img/8967030e0b5ce10b20bf671c636de002.jpg) We can see a pattern here. For some ![k](img/a1c2f8d5b1226e67bdb44b12a6ddf18b.jpg), ![k%20%5Cgt%201](img/d93e8f808c1e0d8331d25c76518435e0.jpg), we have, ![%0A%20%20T%28n%2Ck%29%20%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B%20%5Cmathbb%7BC%7D_%7Bk%7D%20%7D%20%7D%20%5Crfloor%20%28%20T%28n%2C%20k-1%29%20%2B%201%29%20%2B%202%0A](img/ba35d7780bc9e95b897394bfe1ec3cc7.jpg) Here, ![%5Cmathbb%7BC%7D_%7Bk%7D](img/1f0b313b5b7c582dfebfcf2ce9a22797.jpg) is the ![k%5E%7Bth%7D](img/14cfbbd01193894e0a873225bff61f8f.jpg) coin denomination. We can expand this further: ![%0A%20%20T%28n%2Ck%29%0A%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B%20%5Cmathbb%7BC%7D_%7Bk%7D%20%7D%20%7D%20%5Crfloor%20%28%20T%28n%2C%20k-1%29%20%2B%201%20%29%20%2B%202%0A%3D%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B%20%5Cmathbb%7BC%7D_%7Bk%7D%20%7D%20%7D%20%5Crfloor%0A%28%20%5Clfloor%20%7B%5Cfrac%20%7Bn%7D%20%7B%20%5Cmathbb%7BC%7D_%7Bk-1%7D%20%7D%20%20%7D%20%5Crfloor%0A%28...%20%5Clfloor%20%5Cfrac%20%7Bn%7D%20%7B%20%5Cmathbb%7BC%7D_%7B2%7D%20%7D%20%5Crfloor%20%282n%2B1%29%20...%29%0A%29%20%2B%202%0A%3D%20%5CTheta%28n%5Ek%29%0A](img/53a68028004330cd0f704246876060a3.jpg) Note that the actual values of the coin denominations have no effect on the order of growth of this process, if we assume they are constants that do not depend on `n` and `k`.</solution></exercise><exercise>The sine of an angle (specified in radians) can be computed by making use of the approximation ![%5Csin%20x%5Capprox%20x](img/508b0d57d229069e9f8a8796664bd2ae.jpg) if ![x](img/40779fc60a53ff2b70f832ec10cade09.jpg) is sufficiently small, and the trigonometric identity ![%5Csin%20x%3D3%5Csin%20%7B%5Cfrac%7Bx%7D%7B3%7D%7D-4%5Csin%5E3%7B%5Cfrac%7Bx%7D%7B3%7D%7D](img/2db557906afe6f9edff8f329432809ba.jpg) to reduce the size of the argument of ![%5Csin](img/9fef2f84ecb647520900d178f467bd69.jpg). (For purposes of this exercise an angle is considered <quote>sufficiently small</quote> if its magnitude is not greater than 0.1 radians.) These ideas are incorporated in the following functions:

```js
function cube(x) {
    return x * x * x;
}
function p(x) {
    return 3 * x - 4 * cube(x);
}
function sine(angle) {
    return !(abs(angle) > 0.1)
           ? angle
           : p(sine(angle / 3.0));
}
```

1.  计算`sine(12.15)`时，函数`p`被应用多少次？
2.  评估`sine(a)`时，`sine`函数生成的过程使用的空间增长顺序和步数（作为![a](img/070b1af5eca3a5c5d72884b536090f17.jpg)的函数）是什么？

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_14_2_div">Solution</button> <solution>1.  只要角度值大于0.1，函数`p`就会递归调用自身。 总共将有5个`p`调用，参数为12.15、4.05、1.35、0.45、0.15和0.05。
2.  函数`sine`引起递归过程。 在每个递归调用中，将`angle`除以3，直到其绝对值小于0.1。 因此，步骤数和所需空间具有![O%28%5Clog%20a%29](img/632af215e0aca047dea872e14c2e0af9.jpg)的增长顺序。 请注意，对数的底数对于增长顺序并不重要，因为不同底数的对数仅相差一个常数。</solution></exercise> 

* * *

[[1]](14#footnote-link-1) These statements mask a great deal of oversimplification. For instance, if we count process steps as <quote>machine operations</quote> we are making the assumption that the number of machine operations needed to perform, say, a multiplication is independent of the size of the numbers to be multiplied, which is false if the numbers are sufficiently large. Similar remarks hold for the estimates of space. Like the design and description of a process, the analysis of a process can be carried out at various levels of abstraction.

