功能
f
由f(n) = n
n < 3
和f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3)
n > 3
的规则定义。编写一个通过递归过程计算f
的过程。编写一个通过迭代过程计算f
的过程。
递归地实现它很简单。但我无法弄清楚如何迭代地做到这一点。我尝试与给出的Fibonacci示例进行比较,但我不知道如何将其用作类比。所以我放弃了(羞辱我)并用Google搜索an explanation,我发现了这个:
(define (f n)
(if (< n 3)
n
(f-iter 2 1 0 n)))
(define (f-iter a b c count)
(if (< count 3)
a
(f-iter (+ a (* 2 b) (* 3 c))
a
b
(- count 1))))
阅读之后,我理解代码及其工作原理。但我不明白的是从函数的递归定义到此需要的过程。我不明白代码是如何在某人的头脑中形成的。
您能解释一下解决方案所需的思考过程吗?
答案 0 :(得分:29)
您需要捕获某些累加器中的状态并在每次迭代时更新状态。
如果您有使用命令式语言的经验,请设想编写while循环并在循环的每次迭代期间跟踪变量中的信息。你需要什么变量?你会如何更新它们?这正是你在Scheme中进行迭代(尾递归)调用的必要条件。
换句话说,开始将其视为while循环而不是递归定义可能会有所帮助。最终你会用递归的流畅 - &gt;迭代转换,你不需要额外的帮助来开始。
对于这个特定的例子,你必须仔细观察三个函数调用,因为它不能立即清楚如何表示它们。然而,这是可能的思考过程:(在Python伪代码中强调命令性)
每个递归步骤都会跟踪三件事:
f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3)
所以我需要三个状态来跟踪f
的当前,最后和倒数第二个值。 (即f(n-1), f(n-2) and f(n-3)
。)将其称为a, b, c
。我必须在每个循环中更新这些部分:
for _ in 2..n:
a = NEWVALUE
b = a
c = b
return a
那么什么是NEWVALUE?好吧,现在我们有f(n-1), f(n-2) and f(n-3)
的表示,它只是递归方程式:
for _ in 2..n:
a = a + 2 * b + 3 * c
b = a
c = b
return a
现在剩下的就是找出a, b and c
的初始值。但这很容易,因为我们知道f(n) = n if n < 3
。
if n < 3: return n
a = 2 # f(n-1) where n = 3
b = 1 # f(n-2)
c = 0 # f(n-3)
# now start off counting at 3
for _ in 3..n:
a = a + 2 * b + 3 * c
b = a
c = b
return a
这仍然与Scheme迭代版本略有不同,但我希望你现在可以看到思考过程。
答案 1 :(得分:14)
我认为你问的是如何在“设计模式”之外自然地发现算法。
在每个n值处查看f(n)的扩展是有帮助的:
f(0) = 0 |
f(1) = 1 | all known values
f(2) = 2 |
f(3) = f(2) + 2f(1) + 3f(0)
f(4) = f(3) + 2f(2) + 3f(1)
f(5) = f(4) + 2f(3) + 3f(2)
f(6) = f(5) + 2f(4) + 3f(3)
仔细观察f(3),我们发现我们可以从已知值立即计算出来。 我们需要计算f(4)?
我们至少需要计算f(3)+ [其余]。但是当我们计算f(3)时,我们也计算f(2)和f(1),我们碰巧需要计算f(4)的[其余]。
f(3) = f(2) + 2f(1) + 3f(0)
↘ ↘
f(4) = f(3) + 2f(2) + 3f(1)
因此,对于任何数字n,我可以从计算f(3)开始,并重用我用来计算f(3)的值来计算f(4)......并且模式继续...
f(3) = f(2) + 2f(1) + 3f(0)
↘ ↘
f(4) = f(3) + 2f(2) + 3f(1)
↘ ↘
f(5) = f(4) + 2f(3) + 3f(2)
由于我们将重用它们,让我们给它们一个名字a,b,c。下载我们所处的步骤,并完成f(5)的计算:
Step 1: f(3) = f(2) + 2f(1) + 3f(0) or f(3) = a1 + 2b1 +3c1
,其中
a 1 = f(2)= 2,
b 1 = f(1)= 1,
c 1 = 0
因为f(n)= n,n 因此: f(3)= a 1 + 2b 1 + 3c 1 = 4 所以: a 2 = f(3)= 4(上面在步骤1中计算), b 2 = a 1 = f(2)= 2, c 2 = b 1 = f(1)= 1 因此: f(4)= 4 + 2 * 2 + 3 * 1 = 11 所以: a 3 = f(4)= 11(上面在步骤2中计算), b 3 = a 2 = f(3)= 4, c 3 = b 2 = f(2)= 2 因此: f(5)= 11 + 2 * 4 + 3 * 2 = 25 在上面的计算中,我们捕获先前计算中的状态并将其传递给下一步,
particularily: 步骤 =步骤1的结果 b step = a step - 1 c 步骤 = b 步骤-1 一旦我看到这一点,那么提出迭代版本就很简单了。 Step 2: f(4) = f(3) + 2a1 + 3b1
Step 3: f(5) = f(4) + 2a2 + 3b2
答案 2 :(得分:2)
由于你链接的帖子描述了很多关于解决方案的内容,我将尝试仅提供补充信息。
在这里,你试图在Scheme中定义一个尾递归函数,给定一个(非尾部)递归定义。
递归的基本情况(f(n)= n,如果n <3)由两个函数处理。我不确定作者为什么会这样做;第一个功能可能只是:
(define (f n)
(f-iter 2 1 0 n))
一般形式是:
(define (f-iter ... n)
(if (base-case? n)
base-result
(f-iter ...)))
注意我还没有填写f-iter的参数,因为你首先需要了解需要从一次迭代传递到另一次迭代的状态。
现在,让我们看一下f(n)的递归形式的依赖关系。它引用f(n - 1),f(n - 2)和f(n - 3),因此我们需要保持这些值。当然我们需要n本身的值,所以我们可以停止迭代它。
这就是你如何提出尾递归调用:我们计算f(n)用作f(n - 1),将f(n - 1)旋转到f(n - 2)和f(n) - 2)到f(n - 3),并递减计数。
如果这仍然无济于事,请尝试提出一个更具体的问题 - 如果您已经写了“我不明白”,那么很难回答这个问题。
答案 3 :(得分:1)
我将采用与此处其他答案略有不同的方法来解决这个问题,重点关注编码风格如何使像这样的算法背后的思维过程更容易理解。
问题中引用Bill's approach的问题在于,目前尚不清楚状态变量a
,b
传达的含义是什么和c
。他们的名字没有传达信息,比尔的帖子没有描述他们遵守的任何不变或其他规则。如果状态变量遵循描述它们之间关系的一些记录规则,我发现制定和理解迭代算法都更容易。
考虑到这一点,请考虑完全相同算法的替代公式,这与Bill的不同之处仅在于为a
,b
和c
提供更有意义的变量名称并且增加计数器变量而不是递减的:
(define (f n)
(if (< n 3)
n
(f-iter n 2 0 1 2)))
(define (f-iter n
i
f-of-i-minus-2
f-of-i-minus-1
f-of-i)
(if (= i n)
f-of-i
(f-iter n
(+ i 1)
f-of-i-minus-1
f-of-i
(+ f-of-i
(* 2 f-of-i-minus-1)
(* 3 f-of-i-minus-2)))))
突然间,算法的正确性 - 以及其创建背后的思维过程 - 很容易看到和描述。要计算f(n)
:
i
,从2开始爬升到n
,每次调用f-iter
时递增1。f(i)
,f(i-1)
和f(i-2)
,这足以让我们计算f(i+1)
。i=n
,我们就完成了。答案 4 :(得分:1)
功能
f
由f(n) = n, if n<3
和f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3), if n > 3
规则定义。编写一个通过递归过程计算f
的过程。
已写入:
f(n) = n, (* if *) n < 3
= f(n - 1) + 2f(n - 2) + 3f(n - 3), (* if *) n > 3
信不信由你,曾经such a language。用另一种语言写下这只是语法问题。顺便说一句,你(错误)引用它的定义有一个错误,现在非常明显和明确。
编写一个通过迭代过程计算
f
的过程。
迭代意味着going forward(有你的解释!)而不是递归首先向向后:
f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3)
= a + 2b + 3c
f(n+1) = f(n ) + 2f(n - 1) + 3f(n - 2)
= a' + 2b' + 3c' a' = a+2b+3c, b' = a, c' = b
......
这样就将问题的状态转换描述为
(n, a, b, c) -> (n+1, a+2*b+3*c, a, b)
我们可以将其编码为
g (n, a, b, c) = g (n+1, a+2*b+3*c, a, b)
但当然不会停止。所以我们必须改为
f n = g (2, 2, 1, 0)
where
g (k, a, b, c) = g (k+1, a+2*b+3*c, a, b), (* if *) k < n
g (k, a, b, c) = a, otherwise
这已经完全像你提到的代码一样,直到语法。
计算到 n 在这里更自然,遵循我们的“前进”范例,但是倒数到 0 ,因为你引用的代码当然完全是等效。
角落案件和可能的一对一错误被排除在练习非有趣的技术背景之外。
答案 5 :(得分:0)
是什么帮助我使用铅笔手动运行了该过程,并使用了提示作者作为斐波那契示例提供的信息
a <- a + b
b <- a
将其转化为新问题是您如何在此过程中推动状态
a <- a + (b * 2) + (c * 3)
b <- a
c <- b
因此,您需要一个带有接口的函数来接受3个变量:a, b, c
。它需要使用上面的过程来调用自己。
(define (f-iter a b c)
(f-iter (+ a (* b 2) (* c 3)) a b))
如果您运行并打印从(f-iter 1 0 0)
开始的每个迭代的每个变量,您将得到类似的信息(它将永远运行):
a b c
=========
1 0 0
1 1 0
3 1 1
8 3 1
17 8 3
42 17 8
100 42 17
235 100 42
...
您看到答案了吗?您可以通过对每次迭代的b和c列求和来获得它。我必须承认我是通过一些跟踪和错误发现的。剩下的就是要知道何时停止的计数器,这是整个过程:
(define (f n)
(f-iter 1 0 0 n))
(define (f-iter a b c count)
(if (= count 0)
(+ b c)
(f-iter (+ a (* b 2) (* c 3)) a b (- count 1))))