Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scheme SICP Ex.1.17-“文件”;“快速乘法”;慢于;乘“;?_Scheme_Lisp_Multiplication_Sicp_Mit Scheme - Fatal编程技术网

Scheme SICP Ex.1.17-“文件”;“快速乘法”;慢于;乘“;?

Scheme SICP Ex.1.17-“文件”;“快速乘法”;慢于;乘“;?,scheme,lisp,multiplication,sicp,mit-scheme,Scheme,Lisp,Multiplication,Sicp,Mit Scheme,以下函数作为本练习的介绍,说明了用加法定义的乘法。这是最简单的“易于写下来”递归定义 (定义(星号a和b) (如果(=b 0) 0 (+a(星a(-B1(())))) 在前面的练习之后,我看到的第一件事是编写一个不会破坏堆栈的迭代形式: (定义(星号a和b) (国际热核实验堆star a b 0)) (定义(star iter a计数器和) (如果(=计数器0) 总和 (国际热核实验堆a(计数器1)(+a总和))) 练习1.17鼓励我们找到一个不变量,以减少问题的规模,其思想是从O(n)到O

以下函数作为本练习的介绍,说明了用加法定义的乘法。这是最简单的“易于写下来”递归定义

(定义(星号a和b)
(如果(=b 0)
0
(+a(星a(-B1(()))))
在前面的练习之后,我看到的第一件事是编写一个不会破坏堆栈的迭代形式:

(定义(星号a和b)
(国际热核实验堆star a b 0))
(定义(star iter a计数器和)
(如果(=计数器0)
总和
(国际热核实验堆a(计数器1)(+a总和)))
练习1.17鼓励我们找到一个不变量,以减少问题的规模,其思想是从O(n)到O(logn)步数(在执行该特定步骤时,不做任何事情来更新结果-我们在该步骤中所做的只是减少/转换问题定义-这就是“查找不变量”的含义)(参见下面第一个代码块的第3行-在该步骤中没有向结果添加任何内容)

对于快速版本,问题是我们应该使用函数
halve
double
,这似乎意味着这些函数可以作为机器操作(恒定时间?)使用。我已经实现了“快速”版本,只需对这些函数进行如下欺骗:

(定义(快星a b)
(条件((或(=B0)(=A0))0)
((偶数?a)(快星(/a2)(*2b)))
(其他(+a(快星a(-B1()()))))
迭代形式(即O(1)空间)中的相同内容:

(注意上面第4行的
+a
如何移动到下面第6行末尾的累加器,以使其处于尾部位置)

(定义(快星b)
(快星iter a b 0)
(定义(快速星iter a b和)
(cond((或(=a0)(=b0))和)
((偶数a)(快星iter(/A2)(*2B)和))
(其他(快星国际热核实验堆a(-B1)(+a总和(()))))
所以这是一个“有什么意义”的问题-这些函数比上面给出的前两个慢。这四个函数中的第一个会破坏堆栈,所以它没有用处。但第二个没有。这一个比我测试的这两个“快速”版本中的任何一个都快

我在这里遗漏了什么吗?奇怪的是,是否有一种方法可以实现
减半
加倍
,所以他们实际上给出了这里建议的log(n)结果。否则,这个问题肯定没有意义


请注意,如果a&b的大小不同,那么它们的顺序非常重要-比如乘以2100倍或100,2倍,第一个是100步,后两个是2步。这将是以后添加到这个函数中的一些内容。但是好奇的是
halve
double

我认为主要的问题是
b
既递减又加倍,即,
b
应减半,而不是
a
。目前2*100将变为1*200,需要200次递减而不是100次。而if应变为4*50,然后8*25

此外,如果我们减少奇数,结果将是偶数,因此下一步将把
b
的值减半。也就是说,至少有一半的迭代将
b
的值减半


例如,如果
b
<1048576(2^20),则步数应小于40。通常迭代次数小于(*2(log b2))。

您的代码中有一个细微的错误,这就是它速度慢的原因。对于版本3和版本4,这应该可以修复它:

(define (fast-star a b)
  (cond ((or (= b 0) (= a 0)) 0)
        ((even? b) (fast-star (* 2 a) (/ b 2.0)))
        (else (+ a (fast-star a (- b 1))))))

(define (fast-star-iter a b sum)
  (cond ((or (= a 0) (= b 0)) sum)
        ((even? b) (fast-star-iter (* 2 a) (/ b 2.0) sum))
        (else (fast-star-iter a (- b 1) (+ a sum)))))
我们的想法是在每次迭代中不断增加
a
和减少
b
,但根据具体情况,有时会减少
b
,有时会增加一倍!还要注意,我将
b
除以
2.0
,以摆脱精确的算法,这会更慢


当然,你可以用另一种方法来做事情:增加
b
和减少
a
——重要的是保持一致,将一个参数的问题减半,将另一个参数翻倍,我们需要在最终结果中增加一个g公式

a*n = a+a*(n-1)
a*n = a*(n/2)+a*(n/2)
你应该使用这个公式

a*n = a+a*(n-1)
a*n = a*(n/2)+a*(n/2)

注意
n
偶数
且n为
奇数
时的情况。应用此选项将使您获得
O(logn)
复杂性,而不是
O(n)

除了答案中指出的问题外,请注意,
halve
double
对于fixnum->fixnum的情况都是单位移位。我希望几乎所有CPU都有这样的操作。
(快星(/a2)(*2b))
应该是
(*2(快星(/a2)b))
。这就是答案。除以2和除以2.0之间有什么区别?这一区别背后的算法会发生什么情况?@salvarico当我们除以
2
时,我们在进行整数除法,Scheme会将值表示为分数,一个精确的值,计算起来会比较慢。当我们除以
2.0
时,我们正在进行浮点除法,Scheme将把值表示为十进制数,这是一个不精确的值,计算起来会更快。哦,我理解这一点。但是,由于我们只在b为偶数时除以2,因此它总是得到一个整数。然而,我仍然想知道进行整数除法和do除法会有什么不同使用十进制数进行除法(但由于它是整数,所以也是精确的)。在内存和时间方面有差异吗?是的,会有差异。当你用
2.0
除法时,结果不再是整数!即使它是精确运算。例如:
(/10 2.0)
将返回
5.0
,而不是
5
。浮点值是一种不同的数据类型,没有rational的开销,这是进行除法下注时得到的结果