标签:
所谓的递归 ,就是函数自己直接或者间接的调用自己。复杂算法通常比较容易使用递归实现 从前有座山,山里有座庙,庙里有个老和尚讲故事,从前有座山,山里有座庙,庙里。。。 这个故事就是现实中递归的一个例子,循环往复,生生不息。
以下就是递归函数最简单的一个例子
function foo(n){ return n + foo(n-1); } foo();
递归中最重要的就是如何跳出循环,因为只有程序跳出了才有结果。如果定义错误,或者缺少终结条件 可导致冻结用户界面
递归的思想就是划归思想。写一个递归函数调用自己,最终还是要转换为自己的这个函数。
假如有一个函数fn,如果它是递归函数的话,也就是说这个函数体内的问题还是转换为fn的形式。
递归思想就是将一个问题转化为一个已解决的问题来实现
案例1:求1到100的和
常规求法,通过for循环
var sum =0; for(var i=1;i<=100;i++){ sum+=i; } console.log(sum);
递归函数方法:
1.首先假定递归函数已经写好,假设是函数fn,也就是说fn(100)就是求1到100的和 2.寻找递推关系,就是n和n-1,或者n-2之间的关系,fn(n)==n+fn(n-1);
var sum = fn(100); sum =fn(99)+100;
3.将递推结构转换为递归体
function fn(n){ return n+fn(n-1); }
4.将临界条件加到递归体中
function fn(n){ if(n==1) return 1; return fn(n-1)+n; }
案例2:数列: 1, 1, 2, 4, 7, 11, 16, … 求 第 n 项, 求前 n 项和.
分析过程 1.假设已经得到结果为函数fn,fn(10),就是第10项 2.找递推关系
* 1, 2 => fn( 1 ) + 0 = fn( 2 ) * 2, 3 => fn( 2 ) + 1 = fn( 3 ) * 3, 4 => fn( 3 ) + 2 = fn( 4 ) * ... * n-1, n => fn( n-1 ) + n - 2 = fn( n )
2.临界条件 n == 1 => 1 3.那么递归体就清楚了,临界条件就是n ==1 =>1
function fn(n){ if(n==1) return 1; return fn(n-1)+n-2; }
前n项的和
function sum(n){ if(n==1) return 1; retrun sum(n-1)+fn(n); }
案例3:Fibonacci 数列: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, … 求其第 n 项.
分析过程: 1.假设已经得到结果为函数fib,fib(10),就是第10项 2.找递推关系
2, 3 => fib( 1 ) + fib(2) = fib( 3 ) 3, 4 => fib( 2 ) + fib(3) = fib( 4 ) ... 9, 10 => fib(8)+fib(9) = fib(10) n-1, n => fib( n-2 ) + fib(n - 1) = fib( n )
2.临界条件 n == 1||n==2 => 1
function fib(n){ if(n==1||n==2) return 1; return fib(n-2)+fib(n-2); }
这里又涉及到另一个问题,递归函数的性能问题。 假如我们用递归的方法求斐波那契数列前n项的和,应该怎么计算?
设置一个变量count
var count=0; function fib(n){ count++; if(n==1||n==2) return 1; return fib(n-2)+fib(n-2); }
// sum(5)时,count为14; // sum(10),count为113次 // sum(20),count为4071次 // sum(40),count为4194259次
可以看出,随着求和数字的增大,递归函数的计算次数几乎爆炸性增加。 如果n为成千上万的时候,sum(n)成了不可完成的任务。
* 递归函数求和性能低的原因是重复计算,如果每次将计算的结果存储起来。 * 再每次需要的时候先看有没有存储过该数据,如果有这个数据,直接拿出来使用 * 如果没有再递归,仍旧把计算的结果再次存储起来,以便下次使用。
斐波那契数列求n项代码优化如下:
var data=[1,1]; function fib2(n){ //先看看数组中有没有 var v = data[n]; if(v===undefined){ //这里用递归求解 if(n==1||n==2) return 1; v = fib2(n-1)+fib2(n-2); data[n]=v; }else { //数组有就直接返回v return v; } }
通过优化后的代码来计算次数
var count2 = 0; function fib2( n ) { count2++; var v = data[ n ]; if ( v === undefined ) { v = fib2( n-1 ) + fib2( n-2 ); data[ n ] = v; } return v; }
* // sum(5)时,count为12; ` * // sum(10),count为27次` * // sum(20),count为57次` * // sum(40),count为117次`
可以看出通过数据的缓存,能极大的减少计算次数,提高递归函数的性能。
减少工作量就是最好的性能优化技术,代码所做的事情越少,它的运行速度就越快。根据这些原则,避免 重复工作很有意义。而多次执行相同的任务也是浪费时间。通过缓存先前的计算结果为后续的计算所用, 避免了重复工作。
标签:
原文地址:http://www.cnblogs.com/newwebdeveloper/p/5768963.html