如何改进这段代码?
(define (f n)
(if (< n 3)
n
(+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3))))
))
正如预期的那样,诸如 (f 100) 之类的评估需要很长时间。 我想知道是否有一种方法可以改进此代码(无需放弃递归)和/或利用多核盒。 我正在使用“mit-scheme”。
My solution to exercise 1.11 of SICP is:
(define (f n)
(if (< n 3)
n
(+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3))))
))
As expected, a evaluation such as (f 100) takes a long time. I was wondering if there was a way to improve this code (without foregoing the recursion), and/or take advantage of multi-core box. I am using 'mit-scheme'.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
该练习要求您编写两个函数,一个“通过递归过程”计算
f
,另一个“通过迭代过程”计算f
。 你做了递归一件事。 由于此函数与您链接到的部分示例中给出的 fib 函数非常相似,因此您应该能够通过查看 fib 的递归和迭代示例来弄清楚这一点 函数:在本例中,您将定义一个
f-iter
函数,该函数将采用a
、b
和c
参数以及count
参数。这是
f-iter
函数。 请注意与fib-iter
的相似之处:通过一点试验和错误,我发现
a
、b
和c< /code> 应分别初始化为
2
、1
和0
,这也遵循fib
的模式> 函数将a
和b
初始化为1
和0
。 所以f
看起来像这样:注意:
f-iter
仍然是一个递归函数,但是由于Scheme 的工作方式,它作为迭代运行进程并在O(n)
时间和O(1)
空间中运行,与您的代码不同,它不仅是一个递归函数,而且是一个递归函数过程。 我相信这就是练习 1.1 的作者所寻找的。The exercise tells you to write two functions, one that computes
f
"by means of a recursive process", and another that computesf
"by means of an iterative process". You did the recursive one. Since this function is very similar to thefib
function given in the examples of the section you linked to, you should be able to figure this out by looking at the recursive and iterative examples of thefib
function:In this case you would define an
f-iter
function which would takea
,b
, andc
arguments as well as acount
argument.Here is the
f-iter
function. Notice the similarity tofib-iter
:And through a little trial and error, I found that
a
,b
, andc
should be initialized to2
,1
, and0
respectively, which also follows the pattern of thefib
function initializinga
andb
to1
and0
. Sof
looks like this:Note:
f-iter
is still a recursive function but because of the way Scheme works, it runs as an iterative process and runs inO(n)
time andO(1)
space, unlike your code which is not only a recursive function but a recursive process. I believe this is what the author of Exercise 1.1 was looking for.我不确定如何最好地在方案中对其进行编码,但提高此类速度的常见技术是使用 记忆。 简而言之,这个想法是缓存 f(p) 的结果(可能是每个看到的 p,或者可能是最后 n 个值),以便下次调用 f(p) 时,返回保存的结果,而不是重新计算。 一般来说,缓存是从元组(表示输入参数)到返回类型的映射。
I'm not sure how best to code it in Scheme, but a common technique to improve speed on something like this would be to use memoization. In a nutshell, the idea is to cache the result of f(p) (possibly for every p seen, or possibly the last n values) so that next time you call f(p), the saved result is returned, rather than being recalculated. In general, the cache would be a map from a tuple (representing the input arguments) to the return type.
好吧,如果你问我,请像数学家一样思考。 我看不懂方案,但如果您正在编写斐波那契函数,而不是递归地定义它,请解决递归并用封闭形式定义它。 例如,对于斐波那契数列,可以在此处找到闭合形式。 那会快得多。
编辑:哎呀,没看到你说放弃递归。 在这种情况下,你的选择就更加有限。
Well, if you ask me, think like a mathematician. I can't read scheme, but if you're coding a Fibonacci function, instead of defining it recursively, solve the recurrence and define it with a closed form. For the Fibonacci sequence, the closed form can be found here for example. That'll be MUCH faster.
edit: oops, didn't see that you said forgoing getting rid of the recursion. In that case, your options are much more limited.
请参阅本文,了解有关使用函数式编程开发快速斐波那契函数的好教程。 它使用Common LISP,在某些方面与Scheme略有不同,但您应该能够使用它。 您的实现相当于文件顶部附近的
bogo-fig
函数。See this article for a good tutorial on developing a fast Fibonacci function with functional programming. It uses Common LISP, which is slightly different from Scheme in some aspects, but you should be able to get by with it. Your implementation is equivalent to the
bogo-fig
function near the top of the file.换句话说:
为了获得尾递归,递归调用必须是过程的最后一件事。
您的递归调用嵌入在 * 和 + 表达式中,因此它们不是尾部调用(因为 * 和 + 是在递归调用之后求值的。)
Jeremy Ruten 的
f-iter< /code> 是尾递归而不是迭代(即它看起来像递归过程,但与等效迭代一样高效。)
但是您可以使迭代显式化:
或
To put it another way:
To get tail recursion, the recursive call has to be the very last thing the procedure does.
Your recursive calls are embedded within the * and + expressions, so they are not tail calls (since the * and + are evaluated after the recursive call.)
Jeremy Ruten's version of
f-iter
is tail-recursive rather than iterative (i.e. it looks like a recursive procedure but is as efficient as the iterative equivalent.)However you can make the iteration explicit:
or
该特定练习可以通过使用尾递归来解决 - 而不是等待每个递归调用返回(如您提供的简单解决方案中的情况),您可以将答案累积在参数中,从而使递归行为就其消耗的空间而言,与迭代完全相同。 例如:
That particular exercise can be solved by using tail recursion - instead of waiting for each recursive call to return (as is the case in the straightforward solution you present), you can accumulate the answer in a parameter, in such a way that the recursion behaves exactly the same as an iteration in terms of the space it consumes. For instance: