当我来到一个描述函数递归的章节时,我是Javascript的新手并正在阅读它。它使用示例函数来查找第n个Fibonacci序列。代码如下:
function fibonacci(n) {
if (n < 2){
return 1;
}else{
return fibonacci(n-2) + fibonacci(n-1);
}
}
console.log(fibonacci(7));
//Returns 21
我无法准确掌握这个功能正在做什么。有人能解释一下这里发生了什么吗?我被困在第5行,函数调用自己。这里发生了什么?
答案 0 :(得分:86)
您根据自身定义了一个功能。一般来说,fibonnaci(n) = fibonnaci(n - 2) + fibonnaci(n - 1)
。我们只是在代码中表示这种关系。因此,对于fibonnaci(7)
我们可以观察到:
fibonacci(7)
等于fibonacci(6)
+ fibonacci(5)
fibonacci(6)
等于fibonacci(5)
+ fibonacci(4)
fibonacci(5)
等于fibonacci(4)
+ fibonacci(3)
fibonacci(4)
等于fibonacci(3)
+ fibonacci(2)
fibonacci(3)
等于fibonacci(2)
+ fibonacci(1)
fibonacci(2)
等于fibonacci(1)
+ fibonacci(0)
fibonacci(1)
等于1 fibonacci(0)
等于1 我们现在拥有评估fibonacci(7)
所需的所有部分,这是我们最初的目标。请注意,基本情况 - return 1
时n < 2
- 是使这成为可能的原因。这就是停止递归的原因,这样我们就可以开始展开堆栈的过程,并总结我们在每一步返回的值。如果没有这一步,我们会继续在较小和较小的值上调用fibonacci
,直到程序最终崩溃。
添加一些说明这一点的日志语句可能会有所帮助:
function fibonacci(n, c) {
var indent = "";
for (var i = 0; i < c; i++) {
indent += " ";
}
console.log(indent + "fibonacci(" + n + ")");
if (n < 2) {
return 1;
} else {
return fibonacci(n - 2, c + 4) + fibonacci(n - 1, c + 4);
}
}
console.log(fibonacci(7, 0));
输出:
fibonacci(7)
fibonacci(5)
fibonacci(3)
fibonacci(1)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(4)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(3)
fibonacci(1)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(6)
fibonacci(4)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(3)
fibonacci(1)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(5)
fibonacci(3)
fibonacci(1)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(4)
fibonacci(2)
fibonacci(0)
fibonacci(1)
fibonacci(3)
fibonacci(1)
fibonacci(2)
fibonacci(0)
fibonacci(1)
将相同缩进级别的值相加以产生前一级别缩进的结果。
答案 1 :(得分:27)
这里有很多好的答案,但我制作了这个图表,有助于更好地解释函数的结果。将返回的唯一值是1或0(对于n <2,您的示例返回1,但应返回n)。
这意味着每次递归调用最终都会返回0或1.最终会被缓存&#34;在堆栈中&#34;进行了#34;进入原始调用并加在一起。
因此,如果您要为&#39; n&#39;的每个值绘制相同的图表。你可以手动找到答案。
此图大致说明了如何为fib(5)返回每个函数。
这显示了控制流程,即功能的执行顺序。请记住,代码始终是左 - >右,右 - &gt;执行。底部。因此,无论何时调用新函数,它都会暂停,然后进行下一次调用。
以下说明了基于原始帖子的实际控制流程。请注意,基本条件为1. fib(5) {
return fib(4) + fib(3);
2. fib(4) {
return fib(3) + fib(2);
3. fib(3) {
return fib(2) + fib(1);
4. fib(2) {
A= return 1;
};
5. fib(1) {
B= return 1;
};
C= return 2; // (1 + 1)
};
6. fib(2) {
D= return 1;
};
E= return 3; // (2 + 1)
};
7. fib(3) {
return fib(2) + fib(1);
8. fib(2) {
F= return 1;
};
9. fib(1) {
G= return 1;
};
H= return 2; // (1 + 1)
};
I= return 5; // (3 + 2)
};
以简化:
def change_get_to_post(request, **kwargs):
# This is useful when we need to change a potentially large GET request
# into a POST with x-www-form-urlencoded encoding.
if request.method == 'GET' and '?' in request.url:
request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
request.method = 'POST'
request.url, request.data = request.url.split('?', 1)
答案 2 :(得分:20)
步骤1)调用fibonacci(7)
时想象以下内容(注意我如何将所有n更改为7):
function fibonacci(7) {
if (7 < 2){
return 1;
}else{
return fibonacci(7-2) + fibonacci(7-1);
}
}
第2步)由于(7 < 2)
显然是假的,我们转到fibonacci(7-2) + fibonacci(7-1);
,转换为fibonacci(5) + fibonacci(6);
因为fibonacci(5)
首先出现,所以被调用(将n更改为5)这一次):
function fibonacci(5) {
if (5 < 2){
return 1;
}else{
return fibonacci(5-2) + fibonacci(5-1);
}
}
步骤3)或者课程fibonacci(6)
也会被调用,所以发生的事情是每个人调用fibonacci
2个新fibonacci
被调用。
可视化:
fibonacci(7)
____|_____
| |
fibonacci(5) fibonacci(6)
____|____ ____|_____
| | | |
fib(3) fib(4) fib(4) fib(5)
看看它如何分支?什么时候停止?当n
小于2时,这就是if (n < 2)
的原因。此时,分支停止,一切都加在一起。
答案 3 :(得分:5)
希望以下有帮助。主叫:
fibonacci(3)
将到达第5行并执行:
return fibonacci(1) + fibonacci(2);
第一个表达式再次调用该函数并返回1(从n < 2
开始)。
第二个再次调用该函数,到达第5行并执行:。
return fibonacci(0) + fibonacci(1);
两个表达式都返回1(因为n < 2
都是),所以这个函数调用返回2.
所以答案是1 + 2,即3。
答案 4 :(得分:3)
我认为这两个函数对我的递归给出了更明确的解释(来自blog post):
function fibDriver(n) {
return n === 0 ? 0 : fib(0, 1, n);
}
function fib(a, b, n) {
return n === 1 ? b : fib(b, a + b, n-1);
}
答案 5 :(得分:2)
要计算第n个斐波纳契数,关系式为F(n)= F(n-2)+ F(n-1)。
如果我们在代码中实现关系,对于第n个数,我们使用相同的方法计算第(n-2)和第(n-1)个数。
每个后续数字是前两个数字的总和。因此,第七个数字是第六个和第五个数字的总和。更一般地,第n个数是n-2和n-1的总和,只要n> 1。 2.由于递归函数需要停止条件来停止递归,因此n&lt; 2是条件。
f(7)= F(6)+ F(5);
反过来,F(6)= F(5)+ F(4)
F(5)= F(4)+ F(3)...... 它一直持续到n <2
F(1)返回1
答案 6 :(得分:2)
/* * Steps Fibonacci recursion * 1) 3 gets passed. (3 is printed to the screen during this call) * 2) Fibonacci A gets decrements by 2 and recursion happens passing 1 as a param. (1 is printed to the screen during this call) * 3) Fibonacci A hits the base case returning 1 and it "unwinds". (No recursion here) * 4) Fibonacci B gets called, decrementing the previous value of n (3 was the previous value of n before A made the returning call) to 2. (2 is printed to the screen during this call) * 5) Fibonacci A is called again subtracting 2 from n (2-2=0) and passes 0 as a param. (1 is printed to the screen during this call since it's converted from 0) * 6) Fibonacci A hits the base case and "unwinds" (no recursion here) * 7) Fibonacci B is called subtracting 1 from 2 (2 was the previous value of n before A made the returning call) and passes 1 as a param. (1 is printed to the screen during this call) * 7) Fibonacci B now hits the base case, returning 1 and "unwinds" (no recursion here) * 8) Fibonacci B retraces it's steps back through All previous fucntion calls and values of n (n=2 in our case) and adds [them] to the copy of n=1 stored in its local scope * 9) Once Fibonacci B completes the "unwinding" process, it returns the calculated value to the original caller (no recursion here) Note* Each instance of Fibonacci recursion creates its own scope and stores the returned value in a copy of n (in our case 1). As it the function "unwinds" it executes subsequent code that receive the value of n at that time. (all functions that call other functions "unwind" back through previous calls once they return) In the last call of our Fibonacci example, Fibonacci B receives the value of n=2 as Fibonaccci A "unwinds" since that was the last value before it made the returning call. Once Fibonacci B reached the base case and "unwound", it retraced its steps back through all previous values of n (in our case just n=2) and added [them] to its local copy of n=1. * The result when passing the number 3 is: 3 1 2 1 1 (3) */
var div = document.getElementById('fib');
function fib( n, c ) {
var indent = "";
for (var i = 0; i < c; i++) {
indent += " ";
}
var v = n===0 ? 1 : n
var el = document.createElement('div'),
text = indent + "fibonacci(" + v + ")";
el.innerHTML = text;
div.appendChild(el);
if(n<2){
return 1;
}
return fib(n-2, c + 4) + fib(n-1, c + 4);
}
答案 7 :(得分:1)
该函数正在调用自身。这只是递归函数的定义。在第5行中,它通过传递将导致值的参数将执行转移到自身。
为了确保递归函数不会变成无限循环,必须存在不调用自身的某种情况。问题中代码的目标是执行斐波那契序列的计算。
答案 8 :(得分:0)
基于ES6的具有递归函数的斐波那契算法
const fibonacci = ( n, k = 1, fib2 = 0, fib1 = 1 ) => {
return k === n ?
(() => { return fib1; })()
:
(() => {
k++;
return fibonacci(n, k, fib1, fib1 + fib2);
})();
}
console.info(' fibonacci ' + 11 + ' = ' + fibonacci(11));
答案 9 :(得分:0)
看,fib是
t(n)= t(n-1)+ n;
如果n = 0,则为1
因此,让我们看看递归的工作原理,我只是将n
中的t(n)
替换为n-1
,依此类推。它看起来:
t(n-1)= t(n-2)+ n + 1;
t(n-1)= t(n-3)+ n + 1 + n;
t(n-1)= t(n-4)+ n + 1 + n + 2 + n;
。
。
。
t(n)= t(n-k)+ ... +(n-k-3)+(n-k-2)+(n-k-1)+ n;
我们知道t(0)=(n-k)
是否等于1
,然后是n-k=0
,所以n=k
我们将k
替换为n
:
t(n)= t(n-n)+ ... +(n-n + 3)+(n-n + 2)+(n-n + 1)+ n;
如果我们省略n-n
,那么:
t(n)= t(0)+ ... + 3 + 2 + 1 +(n-1)+ n;
所以3+2+1+(n-1)+n
是自然数。计算公式为Σ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2
fib的结果是:O(1 + n²) = O(n²)
这是理解递归关系的最佳方式
答案 10 :(得分:0)
使用递归在JS / ES6中为fib共享一个简单的代码。
function fib(n, first = 1, second = 1) {
if (n <= 2) return 1;
[first, second] = [second, first + second];
return (n - 2 === 1) ? second : fib(n - 1, first, second);
}
console.log(fib(10));