首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >防爆。1.17 -“快速-乘”比“乘”慢?

防爆。1.17 -“快速-乘”比“乘”慢?
EN

Stack Overflow用户
提问于 2020-06-02 10:48:30
回答 3查看 138关注 0票数 2

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

代码语言:javascript
复制
(define (star a b)
  (if (= b 0)
      0
      (+ a (star a (- b 1)))))

当我看到之前的练习之后,我做的第一件事就是写一个不破坏堆栈的迭代形式:

代码语言:javascript
复制
(define (star a b)
  (star-iter a b 0))
(define (star-iter a counter sum)
  (if (= counter 0)
      sum
      (star-iter a (- counter 1) (+ a sum))))

然后,练习1.17鼓励我们找到一个不变量,以减少问题的大小,其思想是从O(n)获得O(N)到O(logn)的步骤数(当执行该特定步骤时,没有做任何事情来更新结果--我们在这一步骤中所做的所有工作就是减少/转换问题定义--即“查找不变”的意思)(参见下面第一个代码块的第3行-该步骤的结果中没有添加任何内容)。

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

代码语言:javascript
复制
(define (fast-star a b)
  (cond ((or (= b 0) (= a 0)) 0)
        ((even? a)      (fast-star (/ a 2) (* 2 b)))
        (else      (+ a (fast-star a       (- b 1))))))

以及同样的迭代形式(即O(1)空间):

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

代码语言:javascript
复制
(define (fast-star b)
  (fast-star-iter a b 0))
(define (fast-star-iter a b sum)
  (cond ((or (= a 0) (= b 0)) sum)
        ((even? a) (fast-star-iter (/ a 2) (* 2 b) sum))
        (else      (fast-star-iter a       (- b 1) (+ a sum)))))

这是一个“重点是什么”的问题--这些函数比上面给出的前两个函数要慢。这四个函数中的第一个函数破坏了堆栈,因此它没有用。但是第二个没有。在我的测试下,这个版本比这两个“快”版本中的任何一个都要快。

我在这里有遗漏什么吗?奇怪的是,是否有一种实现halvedouble的方法,因此它们实际上给出了这里建议的日志(N)结果。否则,这个问题就没有意义了。

注意,如果a&b的顺序是不同的,那么它们的顺序非常重要--例如乘以2,100或100,2倍,第一步是100步,后2步。这将是以后添加到此函数中的内容。但一开始对halvedouble很好奇。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-06-02 13:17:51

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

代码语言:javascript
复制
(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 --重要的是要保持一致,将一个参数的问题减半,将另一个参数的问题加倍,其中一个是我们需要在最终结果中添加的。

票数 3
EN

Stack Overflow用户

发布于 2020-06-02 11:46:37

我认为主要的问题是b正在减少并翻一番,也就是说,b应该减半,而不是a。目前,2* 100将变成1* 200,需要减少200次而不是100次。如果应该变成4* 50然后是8* 25 .

另外,如果我们减少一个奇数,结果将是偶数,所以下一步将把b的值减半。也就是说,至少一半的迭代将使b的值减半

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

票数 1
EN

Stack Overflow用户

发布于 2020-06-03 07:36:46

我们的想法是,与其使用公式

代码语言:javascript
复制
a*n = a+a*(n-1)

你应该用这个公式

代码语言:javascript
复制
a*n = a*(n/2)+a*(n/2)

并注意neven而n为odd的情况。应用这一点将给您带来O(log n)复杂性,而不是O(n)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62150288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档