代码1:
public static int fibonacci (int n){
if (n == 0 || n == 1) {
return 1;
} else {
return fibonacci (n-1) + fibonacci (n-2);
}
}
如果你还没有完成解释它是什么,你如何使用fibonacci
?我已经能够理解在其他情况下使用递归:
代码2:
class two
{
public static void two (int n)
{
if (n>0)
{
System.out.println (n) ;
two (n-1) ;
}
else
{
return ;
}
}
public static void main (String[] arg)
{
two (12) ;
}
}
但是,在代码2的情况下,n
最终将达到不满足n>0
的点,并且该方法将停止递归调用自身。但是,在代码2的情况下,如果n=1
是起点2和3以及5等等,我看不出如何从1获得自己。另外,我看不到行return fibonacci (n-1) + fibonacci (n-2)
如何工作,因为fibonacci (n-2)
必须在某种意义上包含fibonacci (n-1)
以便工作,但它还没有。
我正在看的这本书说它会起作用。它是如何工作的?
答案 0 :(得分:12)
好吧,把编译器实际对你的代码做的事情放在一边(这很糟糕,但很漂亮)以及CPU如何实际解释你的代码(同样),这是一个相当简单的解决方案。
请考虑以下文字说明:
要对编号的块进行排序:
当您收到说明4和5时,系统会要求您重新开始整个过程。但是,这不是问题,因为你仍然知道如何启动这个过程,当它最终完成时,你会得到一堆排序的块。你可以用纸条来说明这些说明,并且它们不会更难以理解。
答案 1 :(得分:3)
在代码2的情况下,虽然n最终会达到不满足n> 0的点,并且该方法将停止递归调用自身
要使其看起来相似,您可以将条件if (n == 0 || n == 1)
替换为if (n < 2)
此外我还没有看到`返回fibonacci(n-1)+ fibonacci(n-2)是如何工作的,因为fibbonacci n-2必须包含某种意义上的fibonacci n-1才能使它变得更好还没有。
我怀疑你想写:“因为fibbonacci n-1 必须包含某种意义上的fibonacci n-2 ”
如果我是对的,那么你将从下面的例子中看到,实际上 fibonacci(n-2)将在每个递归级别被调用两次( fibonacci(1)在例子中):
1.在当前步骤执行 fibonacci(n-2)时
2.在下一步执行 fibonacci((n-1)-1)时
(另请仔细查看Spike's comment)
假设你打电话给 fibonacci(3),那么 fibonacci 的call stack将是这样的:
(Veer提供more detailed explanation)
n=3. fibonacci(3) n=3. fibonacci(2) // call to fibonacci(n-1) n=2. fibonacci(1) // call to fibonacci(n-1) n=1. returns 1 n=2. fibonacci(0) // call to fibonacci(n-2) n=0. returns 1 n=2. add up, returns 2 n=3. fibonacci(1) //call to fibonacci(n-2) n=1. returns 1 n=3. add up, returns 2 + 1
注意,只有在较小args的所有函数返回后才会在 fibonacci(n)中相加(即 fibonacci(n-1), fibonacci (n-2) ... 斐波那契(2),斐波那契(1),斐波那契(0))
要查看call stack更大号码的内容,您可以运行此代码。
public static String doIndent( int tabCount ){
String one_tab = new String(" ");
String result = new String("");
for( int i=0; i < tabCount; ++i )
result += one_tab;
return result;
}
public static int fibonacci( int n, int recursion_level )
{
String prefix = doIndent(recursion_level) + "n=" + n + ". ";
if (n == 0 || n == 1){
System.out.println( prefix + "bottommost level, returning 1" );
return 1;
}
else{
System.out.println( prefix + "left fibonacci(" + (n-1) + ")" );
int n_1 = fibonacci( n-1, recursion_level + 1 );
System.out.println( prefix + "right fibonacci(" + (n-2) + ")" );
int n_2 = fibonacci( n-2, recursion_level + 1 );
System.out.println( prefix + "returning " + (n_1 + n_2) );
return n_1 + n_2;
}
}
public static void main( String[] args )
{
fibonacci(5, 0);
}
答案 2 :(得分:2)
诀窍是,在对fibonacci()的调用返回之前,对fibonacci()的第一次调用才会返回。
你最终调用堆栈上的fibonacci()调用,没有返回,直到你到达n == 0 ||的基本情况n == 1.此时,(可能很大的)fibonacci()调用堆栈开始向第一次调用展开。
一旦你开始思考它,它会很漂亮,直到你的堆栈溢出。
答案 3 :(得分:2)
“如果你还没有完成对Fibonacci的解释,你怎么能使用Fibonacci?”
这是一种质疑递归的有趣方法。这是答案的一部分:当你定义Fibonacci时,尚未定义尚未,但它已被声明。编译器知道有一个名为Fibonacci的东西,它将是int - &gt;类型的函数。 int,并且只要程序运行就将定义。
实际上,这就是C程序中所有标识符的工作方式,而不仅仅是递归标识符。编译器确定已声明的内容,然后通过程序将这些内容的使用指向事物的实际位置(粗略过度简化)。
答案 4 :(得分:2)
让我在考虑n = 3的情况下完成执行。希望它有所帮助。
当n = 3 =&gt;如果条件失败,则执行
return fibonacci (2) + fibonacci (1);
拆分声明:
执行方式(扩展以上四个步骤):
找到斐波那契(2)的价值
找到斐波那契(1)的价值
添加两个值
答案 5 :(得分:1)
尝试自己绘制插图,最终会看到它是如何工作的。需要明确的是,当进行函数调用时,它将首先获取其return
值。简单。
答案 6 :(得分:1)
尝试调试并使用watch来了解变量的状态
答案 7 :(得分:1)
理解递归还需要了解调用堆栈的工作原理,即函数如何相互调用 如果函数没有条件在n == 0或n == 1时停止,则函数将永远递归地调用自身。 它起作用,因为最终,函数将逐渐退出并返回1.此时,返回的fibonacci(n-1)+ fibonacci(n-2)也将返回一个值,并且调用栈真的被清理掉了快。
答案 8 :(得分:0)
我将用一个例子解释你的PC在执行这段代码时所做的事情:
想象一下,你站在一个非常大的房间里。在这个房间旁边的房间里,你有大量的纸张,钢笔和桌子。现在我们要计算斐波那契(3):
我们拿一张桌子把它放在房间的某个地方。在桌子上我们放了一张纸,上面写着“n = 3”。然后我们问自己“嗯,3等于0还是1?”。答案是否定的,所以我们将做“返回fibonacci(n-1)+ fibonacci(n-2);”。
然而,有一个问题,我们不知道“斐波那契(n-1)”和“斐波那契(n-2)”究竟是做什么的。因此,我们再拿两张桌子,将它们放在原始桌子的左右两边,两张纸上写着“n = 2”和“n = 1”。
我们从左表开始,并且想知道“2是否等于0或1?”。当然,答案是否定的,所以我们将再次在此表旁边放置两个表格,其中“n = 1”和“n = 0”。
还在关注?这就是房间的样子:
n = 1的
n = 2 n = 3 n = 1
n = 0的
我们从“n = 1”的表开始,嘿,1等于1,所以我们实际上可以返回有用的东西!我们在另一张纸上写“1”,然后回到表格,上面写着“n = 2”。我们将纸张放在桌子上并转到另一张桌子,因为我们仍然不知道我们将如何处理另一张桌子。
“n = 0”当然也会返回1,所以我们在纸上写下,回到n = 2表并将纸张放在那里。此时,该表上有两篇论文,其中包含“n = 1”和“n = 0”的表的返回值,因此我们可以计算出此方法调用的结果实际为2,所以我们把它写在纸上,然后把它放在桌子上,上面写着“n = 3”。
然后我们一直走到右边的“n = 1”表,然后我们可以立即在纸上写1并将其放回到桌子上,上面写着“n = 3”。在那之后,我们终于有足够的信息说斐波那契(3)返回3。
重要的是要知道你所编写的代码只不过是一个配方。所有编译器都会在您的PC可以理解的另一个配方中转换该配方。如果代码完全是虚假的,就像这样:
public static int NotUseful()
{
return NotUseful();
}
将无休止地循环,或者在我的示例中,您将继续放置越来越多的表而不会实际获得任何有用的表。你的编译器不在乎fibonacci(n-1)或fibonacci(n-2)实际上做什么。