如何计算平方根?最常见的方法是使用牛顿的逐次逼近法,即每当我们对一个数 x 的平方根值有一个猜测 y 时,我们可以通过对 y 与 x / y 求平均值来执行一个简单的操作,以获得一个更好的猜测值(更接近实际的平方根)。 [^(18)](#c1-fn-0018) 例如,我们可以如下计算 2 的平方根。假设我们最初的猜测是 1:
![c1-fig-5006.jpg](../images/c1-fig-5006.jpg)
![c1-fig-5006.jpg](img/c1-fig-5006.jpg)
继续这个过程,我们获得了对平方根越来越好的近似。
...
...
@@ -707,7 +707,7 @@ function sqrt_iter(guess, x) {
这个函数作为一个典型的树递归是有指导意义的,但是它是一个计算斐波那契数的糟糕方法,因为它做了太多多余的计算。请注意图 1.5 中的[和](#c1-fig-0012)中的`fib(3)`的整个计算——几乎一半的工作——都是重复的。事实上,不难看出,函数将计算`fib(1)`或`fib(0)`的次数(一般来说是上述树中的叶子数)恰恰是 Fib( n + 1)。为了了解这有多糟糕,我们可以展示 Fib( n )的值随着 n 呈指数增长。更准确地说(见练习 1.13),Fib( n )是最接近 ϕ ^n / ![c1-fig-5009.jpg](../images/c1-fig-5009.jpg)的整数,其中
这个函数作为一个典型的树递归是有指导意义的,但是它是一个计算斐波那契数的糟糕方法,因为它做了太多多余的计算。请注意图 1.5 中的[和](#c1-fig-0012)中的`fib(3)`的整个计算——几乎一半的工作——都是重复的。事实上,不难看出,函数将计算`fib(1)`或`fib(0)`的次数(一般来说是上述树中的叶子数)恰恰是 Fib( n + 1)。为了了解这有多糟糕,我们可以展示 Fib( n )的值随着 n 呈指数增长。更准确地说(见练习 1.13),Fib( n )是最接近 ϕ ^n / ![c1-fig-5009.jpg](img/c1-fig-5009.jpg)的整数,其中
我们可以用这个定理来得到欧几里德算法的增长阶估计。设 n 为函数的两个输入中较小的一个。如果流程需要 k 步,那么我们必须有 n≥fib(k)≈ϕ^k/![c1-fig-5009.jpg](../images/c1-fig-5009.jpg)。因此步数 k 随着 n 的对数(以 ϕ 为底)增长。因此,增长的顺序是θ(logn)。
我们可以用这个定理来得到欧几里德算法的增长阶估计。设 n 为函数的两个输入中较小的一个。如果流程需要 k 步,那么我们必须有 n≥fib(k)≈ϕ^k/![c1-fig-5009.jpg](img/c1-fig-5009.jpg)。因此步数 k 随着 n 的对数(以 ϕ 为底)增长。因此,增长的顺序是θ(logn)。
##### 练习 1.20
...
...
@@ -1333,7 +1333,7 @@ function gcd(a, b) {
### 示例:测试素性
本节描述了检查整数 n 的素性的两种方法,一种是使用增长顺序θ(![c1-fig-5012.jpg](../images/c1-fig-5012.jpg)),另一种是使用增长顺序θ(logn)的“概率”算法。本节末尾的练习提出了基于这些算法的编程项目。
本节描述了检查整数 n 的素性的两种方法,一种是使用增长顺序θ(![c1-fig-5012.jpg](img/c1-fig-5012.jpg)),另一种是使用增长顺序θ(logn)的“概率”算法。本节末尾的练习提出了基于这些算法的编程项目。
##### 寻找约数
...
...
@@ -1363,7 +1363,7 @@ function is_prime(n) {
}
```
对`find_divisor`的最终测试是基于这样的事实:如果 n 不是质数,那么它必须有一个小于或等于![c1-fig-5012.jpg](../images/c1-fig-5012.jpg)的除数。 [^( 42 )](#c1-fn-0042) 这意味着算法只需要测试 1 和![c1-fig-5012.jpg](../images/c1-fig-5012.jpg)之间的除数。因此,将 n 识别为素数所需的步骤数将具有增长顺序θ(![c1-fig-5012.jpg](../images/c1-fig-5012.jpg))。
对`find_divisor`的最终测试是基于这样的事实:如果 n 不是质数,那么它必须有一个小于或等于![c1-fig-5012.jpg](img/c1-fig-5012.jpg)的除数。 [^( 42 )](#c1-fn-0042) 这意味着算法只需要测试 1 和![c1-fig-5012.jpg](img/c1-fig-5012.jpg)之间的除数。因此,将 n 识别为素数所需的步骤数将具有增长顺序θ(![c1-fig-5012.jpg](img/c1-fig-5012.jpg))。
##### 费马试验
...
...
@@ -1448,7 +1448,7 @@ function report_prime(elapsed_time) {
辛普森法则是一种比上述方法更精确的数值积分方法。使用辛普森法则,函数 f 在 a 和 b 之间的积分近似为
![c1-fig-5018.jpg](../images/c1-fig-5018.jpg)
![c1-fig-5018.jpg](img/c1-fig-5018.jpg)
其中 h=(b–a)/n,对于某些偶数整数 n ,以及 y[k]=f(a+KH)。(增加 n 会增加近似的精确度。)声明一个函数,该函数将参数 f、a 、 b 和 n 作为参数,并返回使用辛普森规则计算的积分值。使用您的函数在 0 和 1 之间积分`cube`(其中 n = 100, n = 1000),并将结果与上面所示的`integral`函数的结果进行比较。
...
...
@@ -1685,7 +1685,7 @@ function sum(term, a, next, b) {
##### 练习 1.31
1. a .`sum`函数只是大量类似抽象中最简单的一个,这些抽象可以被捕捉为高阶函数。写一个类似的函数叫做`product`,它返回一个函数在给定范围内各点的值的乘积。演示如何根据`product`定义`factorial`。同样使用`product`计算 π 的近似值,使用公式[^(52)](#c1-fn-0052)![c1-fig-5019.jpg](../images/c1-fig-5019.jpg)
1. a .`sum`函数只是大量类似抽象中最简单的一个,这些抽象可以被捕捉为高阶函数。写一个类似的函数叫做`product`,它返回一个函数在给定范围内各点的值的乘积。演示如何根据`product`定义`factorial`。同样使用`product`计算 π 的近似值,使用公式[^(52)](#c1-fn-0052)![c1-fig-5019.jpg](img/c1-fig-5019.jpg)
2. b. 如果你的`product`函数生成了一个递归过程,那就写一个生成迭代过程的。如果它生成一个迭代过程,那么就写一个生成递归过程的程序。
控制这种波动的一个方法是防止猜测变化太大。由于答案总是在我们的猜测 y 和 x / y 之间,所以我们可以通过将 y 与 x / y 进行平均来做出一个新的猜测,这个新的猜测距离 y 没有距离 x / y 远,这样在 y 之后的下一个猜测就是![c1-fig-5021.jpg](../images/c1-fig-5021.jpg)(【T21 做出这样一系列猜测的过程,简单来说就是寻找 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)![c1-fig-5021.jpg](../images/c1-fig-5021.jpg)(y+x/y)不动点的过程:
控制这种波动的一个方法是防止猜测变化太大。由于答案总是在我们的猜测 y 和 x / y 之间,所以我们可以通过将 y 与 x / y 进行平均来做出一个新的猜测,这个新的猜测距离 y 没有距离 x / y 远,这样在 y 之后的下一个猜测就是![c1-fig-5021.jpg](img/c1-fig-5021.jpg)(【T21 做出这样一系列猜测的过程,简单来说就是寻找 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)![c1-fig-5021.jpg](img/c1-fig-5021.jpg)(y+x/y)不动点的过程:
```
function sqrt(x) {
...
...
@@ -2026,7 +2026,7 @@ function sqrt(x) {
}
```
(注意 y=![c1-fig-5021.jpg](../images/c1-fig-5021.jpg)(y+x/y)是方程 y=x/y 的简单变换;要导出它,将 y 加到等式的两边,然后除以 2。)
(注意 y=![c1-fig-5021.jpg](img/c1-fig-5021.jpg)(y+x/y)是方程 y=x/y 的简单变换;要导出它,将 y 加到等式的两边,然后除以 2。)
1. a. Suppose that `n` and `d` are functions of one argument (the term index i) that return the N[i] and D[i] of the terms of the continued fraction. Declare a function `cont_frac` such that evaluating `cont_frac(n, d, k)` computes the value of the k-term finite continued fraction. Check your function by approximating 1ϕ using
...
...
@@ -2066,7 +2066,7 @@ function sqrt(x) {
德国数学家 J.H. Lambert 在 1770 年发表了正切函数的连分式表示:
![c1-fig-5025.jpg](../images/c1-fig-5025.jpg)
![c1-fig-5025.jpg](img/c1-fig-5025.jpg)
其中 x 以弧度为单位。声明一个函数`tan_cf(x, k)`,它根据 Lambert 公式计算正切函数的近似值。和练习 1.37 一样,`k`指定了要计算的项数。
我们可以通过再次查看 1.3.3 节末尾描述的定点例子来说明这一点。我们将平方根函数的新版本公式化为定点搜索,从观察到![c1-fig-5005.jpg](../images/c1-fig-5005.jpg)是函数 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x/y 的定点开始。然后我们使用平均阻尼使近似收敛。平均阻尼本身是一种有用的通用技术。也就是说,给定一个函数 f,我们考虑这个函数在 x 的值等于 x 和 f ( x )的平均值。
我们可以通过再次查看 1.3.3 节末尾描述的定点例子来说明这一点。我们将平方根函数的新版本公式化为定点搜索,从观察到![c1-fig-5005.jpg](img/c1-fig-5005.jpg)是函数 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x/y 的定点开始。然后我们使用平均阻尼使近似收敛。平均阻尼本身是一种有用的通用技术。也就是说,给定一个函数 f,我们考虑这个函数在 x 的值等于 x 和 f ( x )的平均值。
我们可以用下面的函数来表达平均阻尼的概念:
...
...
@@ -2109,15 +2109,15 @@ function cube_root(x) {
##### 牛顿方法
当我们第一次介绍平方根函数时,在 1.1.7 节中,我们提到这是牛顿法的特例。如果 x![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)g(x)是一个可微函数,那么方程 g ( x ) = 0 的一个解就是函数 x![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)f(x)的一个不动点其中
当我们第一次介绍平方根函数时,在 1.1.7 节中,我们提到这是牛顿法的特例。如果 x![c1-fig-5022.jpg](img/c1-fig-5022.jpg)g(x)是一个可微函数,那么方程 g ( x ) = 0 的一个解就是函数 x![c1-fig-5022.jpg](img/c1-fig-5022.jpg)f(x)的一个不动点其中
![c1-fig-5026.jpg](../images/c1-fig-5026.jpg)
![c1-fig-5026.jpg](img/c1-fig-5026.jpg)
而 Dg ( x )是在 x 处求的 g 的导数。牛顿法就是我们上面看到的不动点法的运用,通过寻找函数 f 的不动点来逼近方程的一个解。 [^(64)](#c1-fn-0064) 对于许多函数 g 以及对于 x 的足够好的初始猜测,牛顿法非常迅速地收敛到 g ( x ) = 0 的解。 [^(65)](#c1-fn-0065)
为了将牛顿法实现为函数,首先要表达导数的思想。注意,“导数”和平均阻尼一样,是将一个函数转换成另一个函数的东西。例如,函数 xx3 的导数就是函数 x3x2。一般来说,如果 g 是一个函数,而 dx 是一个小数字,那么 g 的导数 Dg 是这样一个函数,它在任意数字 x 处的值由下式给出(在小 dx 的范围内)
设 f 和 g 为两个单参数函数。 g 后的成分 f 定义为函数 x![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)f(g(x))。声明一个实现组合的函数`compose`。例如,如果`inc`是一个参数加 1 的函数,
设 f 和 g 为两个单参数函数。 g 后的成分 f 定义为函数 x![c1-fig-5022.jpg](img/c1-fig-5022.jpg)f(g(x))。声明一个实现组合的函数`compose`。例如,如果`inc`是一个参数加 1 的函数,
```
compose(square, inc)(6);
...
...
@@ -2237,7 +2237,7 @@ compose(square, inc)(6);
##### 练习 1.43
如果 f 是一个数值函数, n 是一个正整数,那么我们就可以形成 f 的第 n 次重复应用,定义为在 x 处的值为 f ( f ( )的函数。。。(f(x)。。。))。例如:如果 f 是函数 x ![c1-fig-5022.jpg](../images/c1-fig-5022.jpg) x + 1,那么 f 的第 n 次重复应用就是函数 x![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x+n。如果 f 是一个数的平方运算,那么 f 的第 n 次重复应用就是将其自变量提升到 2 ^( n ) 次方的函数。编写一个函数,将计算 f 和正整数 n 的函数作为输入,并返回计算第 n 次重复应用 f 的函数。您的函数应该能够按如下方式使用:
如果 f 是一个数值函数, n 是一个正整数,那么我们就可以形成 f 的第 n 次重复应用,定义为在 x 处的值为 f ( f ( )的函数。。。(f(x)。。。))。例如:如果 f 是函数 x ![c1-fig-5022.jpg](img/c1-fig-5022.jpg) x + 1,那么 f 的第 n 次重复应用就是函数 x![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x+n。如果 f 是一个数的平方运算,那么 f 的第 n 次重复应用就是将其自变量提升到 2 ^( n ) 次方的函数。编写一个函数,将计算 f 和正整数 n 的函数作为输入,并返回计算第 n 次重复应用 f 的函数。您的函数应该能够按如下方式使用:
```
repeated(square, 2)(5);
...
...
@@ -2252,7 +2252,7 @@ repeated(square, 2)(5);
##### 练习 1.45
我们在 1.3.3 节中看到,试图通过天真地找到一个固定点 y ![c1-fig-5022.jpg](../images/c1-fig-5022.jpg) x / y 来计算平方根并不收敛,这可以通过平均阻尼来解决。同样的方法也适用于寻找作为平均阻尼的 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x/y²的不动点的立方根。不幸的是,该过程不适用于第四根——单个平均阻尼不足以对 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x/y³收敛进行定点搜索。另一方面,如果我们平均阻尼两次(即,使用平均阻尼的平均阻尼为 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x/y³),定点搜索确实收敛。根据 y![c1-fig-5022.jpg](../images/c1-fig-5022.jpg)x/y^n^(–1)的重复平均阻尼,做一些实验来确定作为定点搜索计算 n 根需要多少个平均阻尼。用这个实现一个简单的函数,用练习 1.43 中的`fixed_point`、`average_damp`和`repeated`函数计算 n 次方根。假设您需要的任何算术运算都可以作为原语获得。
我们在 1.3.3 节中看到,试图通过天真地找到一个固定点 y ![c1-fig-5022.jpg](img/c1-fig-5022.jpg) x / y 来计算平方根并不收敛,这可以通过平均阻尼来解决。同样的方法也适用于寻找作为平均阻尼的 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x/y²的不动点的立方根。不幸的是,该过程不适用于第四根——单个平均阻尼不足以对 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x/y³收敛进行定点搜索。另一方面,如果我们平均阻尼两次(即,使用平均阻尼的平均阻尼为 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x/y³),定点搜索确实收敛。根据 y![c1-fig-5022.jpg](img/c1-fig-5022.jpg)x/y^n^(–1)的重复平均阻尼,做一些实验来确定作为定点搜索计算 n 根需要多少个平均阻尼。用这个实现一个简单的函数,用练习 1.43 中的`fixed_point`、`average_damp`和`repeated`函数计算 n 次方根。假设您需要的任何算术运算都可以作为原语获得。
##### 练习 1.46
...
...
@@ -2348,7 +2348,7 @@ repeated(square, 2)(5);
这个定理在 1845 年被法国数学家和工程师加布里埃尔·拉米证明,他主要以对数学物理的贡献而闻名。为了证明该定理,我们考虑成对(a[k]T5、b[k]T9),其中 a[k]b[k],为此欧几里德算法终止于 k 步。这个证明是基于这样的主张,如果(a[k][+1],b[k][+1])(a[k],b[k])(a[k][-1] 那么我们必须有 b[k][+1]b[k]+b[k][–1]。 为了验证该声明,考虑通过应用变换 a[k][–1]=b[k],b[k][–1]= a[k]除以来定义缩减步长第二个等式是指 a[k]=QB[k]+b[k][–1]对于某正整数 q 。而既然 q 至少必须是 1 我们有 a[k]=QB[k]+b[k][–1]b[k]+b[k 但是在前面的缩减步骤中我们有 b[k][+1]=a[k]。因此,b[k][+1]=a[k]b[k]+b[k][–1]。这证实了这一说法。现在我们可以在 k 上用归纳法证明定理,算法需要终止的步数。对于 k = 1,结果是正确的,因为这仅仅要求 b 至少与 Fib(1) = 1 一样大。现在,假设对于所有小于或等于 k 的整数,结果为真,并为 k + 1 建立结果。让(a[k][+1],b[k][+1])(a[k],b[k])(a[k 通过我们的归纳假设,我们有 b[k][–1]Fib(k–1)和 b[k]Fib(k)。因此,应用我们刚刚证明的要求以及斐波纳契数的定义,给出了 b[k][+1]b[k]+b[k][–1]Fib(k+Fib(]]
[42](#c1-fn-0042a) 如果 d 是 n 的约数,那么 n / d 也是。但是 d 和 n / d 不能都大于![c1-fig-5012.jpg](../images/c1-fig-5012.jpg)。
[42](#c1-fn-0042a) 如果 d 是 n 的约数,那么 n / d 也是。但是 d 和 n / d 不能都大于![c1-fig-5012.jpg](img/c1-fig-5012.jpg)。
@@ -1495,7 +1495,7 @@ function remove(item, sequence) {
“八皇后难题”询问如何将八个皇后放置在棋盘上,使得没有皇后被其他皇后牵制(即,没有两个皇后在同一行、列或对角线上)。图 2.8 显示了一种可能的解决方案。解决这个难题的一个方法是全面展开工作,在每列中放置一个皇后。一旦我们放置了 k–1 张皇后牌,我们必须将 k 张皇后牌放置在一个不会检查棋盘上任何一张皇后牌的位置。我们可以递归地制定这种方法:假设我们已经生成了将 k–1 皇后放置在棋盘的前 k–1 列中的所有可能方式的序列。对于这些方法中的每一种,通过在第 k 列的每一行中放置一个皇后来生成一组扩展的位置。现在过滤这些,只保留第 k 列中的皇后相对于其他皇后安全的位置。这产生了将 k 皇后放置在第一个 k 列中的所有方式的顺序。通过继续这个过程,我们将不仅产生一个解决方案,而是这个难题的所有解决方案。
@@ -2800,7 +2800,7 @@ function div_complex(z1, z2) {
为了使不同的选择具体化,想象有两个程序员,Ben Bitdiddle 和 Alyssa P. Hacker,他们独立地设计复数系统的表示。Ben 选择用矩形来表示复数。有了这个选择,选择一个复数的实部和虚部就简单了,就像用给定的实部和虚部构造一个复数一样。为了找到幅度和角度,或者用给定的幅度和角度构造一个复数,他使用了三角关系
| x = r cos A | r = ![c2-fig-5011.jpg](../images/c2-fig-5011.jpg) |
| x = r cos A | r = ![c2-fig-5011.jpg](img/c2-fig-5011.jpg) |
| y=rsinA | A = arctan( y , x ) |
其将实部和虚部( x 、 y )与幅度和角度( r 、 A )相关联。因此,本的表示由以下选择器和构造器给出:
...
...
@@ -2974,7 +2974,7 @@ function make_from_mag_ang(r, a) {
由此产生的复数系统具有图 2.21 所示的结构。该系统被分解为三个相对独立的部分:复数算术运算、Alyssa 的极坐标实现和 Ben 的直角坐标实现。极坐标和矩形实现可能是由 Ben 和 Alyssa 分别编写的,这两种实现都可以被第三个程序员用作底层表示,以抽象构造器/选择器接口的形式实现复数运算函数。
![c2-fig-0021.jpg](../images/c2-fig-0021.jpg)
![c2-fig-0021.jpg](img/c2-fig-0021.jpg)
[图 2.21](#c2-fig-0031a) 通用复数运算系统的结构。
...
...
@@ -2990,7 +2990,7 @@ function make_from_mag_ang(r, a) {
[图 3.1](#c3-fig-0001) 显示了一个由三个框架组成的简单环境结构,标记为 I、II 和 III。在图中,A、B、C 和 D 是指向环境的指针。c 和 D 指向同一个环境。名字`z`和`x`绑定在第二帧,而`y`和`x`绑定在第一帧,环境 D 中`x`的值为 3。相对于环境 B 的`x`的值也是 3。这确定如下:我们检查序列中的第一帧(帧 III)并且没有找到`x`的绑定,所以我们前进到封闭环境 D 并且在帧 I 中找到绑定。另一方面,环境 A 中的`x`的值是 7,因为序列中的第一帧(帧 II)包含从`x`到 7 的绑定。关于环境 A,帧 II 中`x`到 7 的绑定被说成是遮蔽了帧 I 中`x`到 3 的绑定。
![c3-fig-0001.jpg](../images/c3-fig-0001.jpg)
![c3-fig-0001.jpg](img/c3-fig-0001.jpg)
[图 3.1](#c3-fig-0001a) 一个简单的环境结构。
...
...
@@ -606,7 +606,7 @@ const square = x => x * x;
[图 3.2](#c3-fig-0002) 显示了评估该声明语句的结果。全局环境包含程序环境。为了减少混乱,在这个图之后,我们将不显示全局环境(因为它总是相同的),但是从程序环境向上的指针提醒我们它的存在。函数对象是一对,其代码指定函数有一个参数`x`和一个函数体`**return** x * x;`。函数的环境部分是一个指向程序环境的指针,因为 lambda 表达式就是在这个环境中被求值以产生函数的。程序框架中添加了一个新的绑定,它将函数对象与名称`square`相关联。
![c3-fig-0002.jpg](../images/c3-fig-0002.jpg)
![c3-fig-0002.jpg](img/c3-fig-0002.jpg)
[图 3.2](#c3-fig-0002a) 在程序环境中评估`**function** square(x) { **return** x * x; }`产生的环境结构。
我们可以将原函数连接在一起,构造更复杂的函数。为此,我们将一些功能框的输出连接到其他功能框的输入。例如[图 3.25](#c3-fig-0025) 所示的半加法器电路由一个或门、两个与门和一个反相器组成。它有两个输入信号, A 和 B ,有两个输出信号, S 和 C 。当 A 和 B 中恰好有一个为 1 时 S 变为 1,当 A 和 B 都为 1 时 C 变为 1。从图中可以看出,由于存在延迟,输出可能会在不同的时间生成。数字电路设计中的许多困难都源于这一事实。
@@ -1804,7 +1804,7 @@ function and_gate(a1, a2, output) {
[图 3.27](#c3-fig-0027) 显示了一个纹波进位加法器由 n 个全加法器串接而成。这是并行加法器的最简单形式,用于将两个 n 位二进制数相加。输入一一 [1] ,一一 [2] ,一一 [3] ,。。。、 A [n] 和 B [1、 B [2] 、 B [3] 、。。。、 B [n] 是要相加的两个二进制数(每个 A [k] 和 B [k] 是 0 或 1)。电路产生 S [1] , S [2] , S [3] ,。。。, S [n] ,nn 位的和,以及 C ,加法运算的进位。编写一个生成该电路的函数`ripple_carry_adder`。该函数应采用三个列表作为参数,每个列表包含三根 n 导线,即 A[k]、 B [k] 和 S[k]——以及另一根导线 C 。纹波进位加法器的主要缺点是需要等待进位信号传播。从一个 n 位纹波进位加法器获得完整输出所需的延迟是多少,用与门、或门和反相器的延迟表示?]
![c3-fig-0027.jpg](../images/c3-fig-0027.jpg)
![c3-fig-0027.jpg](img/c3-fig-0027.jpg)
[图 3.27](#c3-fig-0027a) 一个用于 n 位数的纹波进位加法器。
...
...
@@ -2128,7 +2128,7 @@ dAE = FL
这样的约束可以认为是一个由原语加法器、乘法器和常量约束组成的网络([图 3.28](#c3-fig-0028) )。在图中,我们看到左侧的乘法器盒有三个端子,分别标为 m1、m2 和 p 。这些将倍增器连接到网络的其余部分,如下所示: m [1] 端子连接到连接器 C ,该连接器将保持摄氏温度。 m [2] 端子连接到连接器 w ,该连接器也连接到一个容纳 9。乘法器盒约束为 m*[1]和 m*[2]的乘积的 p 端子连接到另一个乘法器盒的 p 端子,其 m [2] 连接到常数 5,其 m [1]**
对并发性的一个可能的限制是规定不能同时发生两个改变任何共享状态变量的操作。这是一个极其严格的要求。对于分布式银行,它要求系统设计者确保一次只能进行一项交易。这既低效又过于保守。[图 3.30](#c3-fig-0030) 显示了彼得和保罗共享一个银行账户,而保罗也有一个私人账户。该图显示了从共享账户中的两次提款(一次由 Peter 提取,一次由 Paul 提取)以及向 Paul 的私人账户中的一次存款。 [^(43)](#c3-fn-0043) 从共享账户的两次提款必须不能并发(因为两者都访问和更新同一个账户),保罗的存款和提款必须不能并发(因为两者都访问和更新保罗钱包中的金额)。但是,允许保罗向他的私人账户存款与彼得从共享账户提款同时进行应该没有问题。
这个定义并不像看起来那么简单,因为我们将通过检查 n 是否能被小于或等于![c3-fig-5001.jpg](../images/c3-fig-5001.jpg)的素数整除来测试一个数 n 是否是素数:
这个定义并不像看起来那么简单,因为我们将通过检查 n 是否能被小于或等于![c3-fig-5001.jpg](img/c3-fig-5001.jpg)的素数整除来测试一个数 n 是否是素数:
```
function is_prime(n) {
...
...
@@ -3459,7 +3459,7 @@ function is_prime(n) {
}
```
这是一个递归定义,因为`primes`是根据`is_prime`谓词定义的,谓词本身使用`primes`流。这个函数起作用的原因是,在任何时候,已经生成了足够多的`primes`流来测试我们接下来需要检查的数字的素性。也就是说,对于我们测试的每一个 n ,要么 n 不是素数(在这种情况下,已经生成了一个素数将其除),要么 n 是素数(在这种情况下,已经生成了一个素数——即,小于 n 的素数——大于![c3-fig-5001.jpg](../images/c3-fig-5001.jpg))。 [^(65)](#c3-fn-0065)
这是一个递归定义,因为`primes`是根据`is_prime`谓词定义的,谓词本身使用`primes`流。这个函数起作用的原因是,在任何时候,已经生成了足够多的`primes`流来测试我们接下来需要检查的数字的素性。也就是说,对于我们测试的每一个 n ,要么 n 不是素数(在这种情况下,已经生成了一个素数将其除),要么 n 是素数(在这种情况下,已经生成了一个素数——即,小于 n 的素数——大于![c3-fig-5001.jpg](img/c3-fig-5001.jpg))。 [^(65)](#c3-fn-0065)
##### 练习 3.53
...
...
@@ -3539,7 +3539,7 @@ function expand(num, den, radix) {
@@ -3549,9 +3549,9 @@ function expand(num, den, radix) {
c + a0x + a1x2 + a2x3 + a3x4 + · · ·
```
其中 c 是任意常数。定义一个函数`integrate_series`,它将流 a [0] , a [1] , a [2] ,作为输入。。。代表一个幂级数和返回流一个一个 [0] ,![c3-fig-5004.jpg](../images/c3-fig-5004.jpg)一个, [1] ,![c3-fig-5005.jpg](../images/c3-fig-5005.jpg),一个, [2] ,。。。级数的积分的非常数项的系数。(由于结果没有常数项,所以不代表一个幂级数;当我们使用`integrate_series`时,我们将使用`pair`将适当的常量连接到流的开头。)
其中 c 是任意常数。定义一个函数`integrate_series`,它将流 a [0] , a [1] , a [2] ,作为输入。。。代表一个幂级数和返回流一个一个 [0] ,![c3-fig-5004.jpg](img/c3-fig-5004.jpg)一个, [1] ,![c3-fig-5005.jpg](img/c3-fig-5005.jpg),一个, [2] ,。。。级数的积分的非常数项的系数。(由于结果没有常数项,所以不代表一个幂级数;当我们使用`integrate_series`时,我们将使用`pair`将适当的常量连接到流的开头。)
2. b. The function x ![c3-fig-5007.jpg](../images/c3-fig-5007.jpg) e^x is its own derivative. This implies that e^x and the integral of e^x are the same series, except for the constant term, which is e⁰ = 1\. Accordingly, we can generate the series for e^x as
2. b. The function x ![c3-fig-5007.jpg](img/c3-fig-5007.jpg) e^x is its own derivative. This implies that e^x and the integral of e^x are the same series, except for the constant term, which is e⁰ = 1\. Accordingly, we can generate the series for e^x as
不幸的是,具有循环的系统的流模型可能需要使用超过目前所见的流编程模式的延迟。例如,[图 3.34](#c3-fig-0039) 所示为求解微分方程 dy/dt=f(y)的信号处理系统,其中 f 为给定函数。该图示出了将 f 应用于其输入信号的映射部件,该映射部件在反馈回路中以非常类似于模拟计算机电路的方式链接到积分器,该模拟计算机电路实际上用于求解这种方程。
@@ -4074,11 +4074,11 @@ function integral(integrand, initial_value, dt) {
考虑设计一个信号处理系统来研究齐次二阶线性微分方程的问题
![c3-fig-5012.jpg](../images/c3-fig-5012.jpg)
![c3-fig-5012.jpg](img/c3-fig-5012.jpg)
建模为 y 的输出流由包含环路的网络生成。这是因为 d²y/dt²的值取决于 y 和 dy / dt 的值,而这两者都是通过对 d²y/dt^(2^(的积分来确定的我们要编码的图表如[图 3.35](#c3-fig-0040) 所示。编写一个函数`solve_2nd`,该函数将常量 a 、 b 和 dt 以及初始值 y [0] 和 dy [0] 作为参数,并生成连续值 y))
![c3-fig-0035.jpg](../images/c3-fig-0035.jpg)
![c3-fig-0035.jpg](img/c3-fig-0035.jpg)
[图 3.35](#c3-fig-0040a) 二阶线性微分方程解的信号流图。
...
...
@@ -4090,7 +4090,7 @@ function integral(integrand, initial_value, dt) {
串联 RLC 电路由一个电阻、一个电容和一个电感串联而成,如图[图 3.36](#c3-fig-0041) 所示。如果 R 、 L 和 C 是电阻、电感和电容,那么这三个元件的电压( v )和电流( i )之间的关系由以下等式描述
编写一个函数`RLC`,将电路的参数 R 、 L 和 C 以及时间增量 dt 作为参数。以类似于练习 3.73 的`RC`函数的方式,`RLC`应该产生一个函数,该函数取状态变量 5 ![c3-fig-5015.jpg](../images/c3-fig-5015.jpg)和![c3-fig-5016.jpg](../images/c3-fig-5016.jpg)的初始值,并产生一对(使用`pair`)状态流 v [ C ] 和 i [ L ] 。使用`RLC`,生成模拟串联 RLC 电路行为的一对流,其中 R = 1 欧姆, C = 0.2 法拉, L = 1 亨利, dt = 0.1 秒,初始值![c3-fig-5016.jpg](../images/c3-fig-5016.jpg) = 0 安培,![c3-fig-5015.jpg](../images/c3-fig-5015.jpg) = 10 伏。
编写一个函数`RLC`,将电路的参数 R 、 L 和 C 以及时间增量 dt 作为参数。以类似于练习 3.73 的`RC`函数的方式,`RLC`应该产生一个函数,该函数取状态变量 5 ![c3-fig-5015.jpg](img/c3-fig-5015.jpg)和![c3-fig-5016.jpg](img/c3-fig-5016.jpg)的初始值,并产生一对(使用`pair`)状态流 v [ C ] 和 i [ L ] 。使用`RLC`,生成模拟串联 RLC 电路行为的一对流,其中 R = 1 欧姆, C = 0.2 法拉, L = 1 亨利, dt = 0.1 秒,初始值![c3-fig-5016.jpg](img/c3-fig-5016.jpg) = 0 安培,![c3-fig-5015.jpg](img/c3-fig-5015.jpg) = 10 伏。
##### 正常顺序评估
...
...
@@ -4226,7 +4226,7 @@ function stream_withdraw(balance, amount_stream) {
另一方面,如果我们仔细观察,我们可以看到与时间相关的问题也悄悄进入功能模型。当我们希望设计交互系统时,尤其是对独立实体之间的交互进行建模时,会出现一个特别麻烦的领域。例如,再次考虑允许联合银行账户的银行系统的实现。在使用赋值和对象的传统系统中,我们将通过让 Peter 和 Paul 向同一个银行帐户对象发送他们的交易请求来模拟 Peter 和 Paul 共享一个帐户的事实,正如我们在 3.1.3 节中看到的。从流的角度来看,这里没有“对象”本身,我们已经指出,银行帐户可以被建模为一个流程,该流程对一个事务请求流进行操作以产生一个响应流。因此,我们可以通过将彼得的交易请求流与保罗的请求流合并,并将结果提供给银行账户流流程,来模拟彼得和保罗拥有联合银行账户的事实,如图[图 3.38](#c3-fig-0043) 所示。
![c3-fig-0038.jpg](../images/c3-fig-0038.jpg)
![c3-fig-0038.jpg](img/c3-fig-0038.jpg)
[图 3.38](#c3-fig-0043a) 一个联名银行账户,通过合并两个交易请求流来建模。
...
...
@@ -4371,7 +4371,7 @@ v_prod(temp1,temp2,答案);
这使用了练习 3.50 中的函数`stream_map_2`。
[65](#c3-fn-0065a) 最后这一点非常微妙,依赖于 p[n][+1]≤![c3-fig-5002.jpg](../images/c3-fig-5002.jpg)。(这里,p[k]表示第 k 个素数。诸如此类的估计很难确定。欧几里德关于素数有无穷多个的古老证明表明,p[n][+1]≤p[1]p[2]p[n]+1,直到 1851 年俄罗斯数学家 P. L .切比雪夫建立了这个结果最初是在 1845 年推测出来的,被称为伯特兰假说。一个证明可以在 Hardy 和 Wright 1960 年的第 22.3 节中找到。
[65](#c3-fn-0065a) 最后这一点非常微妙,依赖于 p[n][+1]≤![c3-fig-5002.jpg](img/c3-fig-5002.jpg)。(这里,p[k]表示第 k 个素数。诸如此类的估计很难确定。欧几里德关于素数有无穷多个的古老证明表明,p[n][+1]≤p[1]p[2]p[n]+1,直到 1851 年俄罗斯数学家 P. L .切比雪夫建立了这个结果最初是在 1845 年推测出来的,被称为伯特兰假说。一个证明可以在 Hardy 和 Wright 1960 年的第 22.3 节中找到。