递归函数初探讨

什么是递归?

所谓的递归 ,就是函数自己直接或者间接的调用自己。复杂算法通常比较容易使用递归实现 从前有座山,山里有座庙,庙里有个老和尚讲故事,从前有座山,山里有座庙,庙里。。。 这个故事就是现实中递归的一个例子,循环往复,生生不息。

以下就是递归函数最简单的一个例子

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(n)

* //   sum(5)时,count为12;    `
* //   sum(10),count为27次`
* //   sum(20),count为57次`
* //   sum(40),count为117次`

可以看出通过数据的缓存,能极大的减少计算次数,提高递归函数的性能。

减少工作量就是最好的性能优化技术,代码所做的事情越少,它的运行速度就越快。根据这些原则,避免 重复工作很有意义。而多次执行相同的任务也是浪费时间。通过缓存先前的计算结果为后续的计算所用, 避免了重复工作。

时间: 2024-12-18 03:47:09

递归函数初探讨的相关文章

spring事务详解(一)初探讨

一.什么是事务 维基百科:数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.理解:事务(Transaction)是数据库区别于文件系统的重要特性之一.传统关系型数据库设计原则是满足 ACID特性,用以保证数据库事务的正确执行.Mysql的innoDB引擎就很好的支持了ACID. 二.事务的ACID特性 (箭头后,翻译自官网介绍:InnoDB and the ACID Model ) 1,原子性(Atomicity):一个事务必须被视为一个不可分割的

(转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对 C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃 C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能

18.如何自学Struts2之Struts2标签和集成初略总结篇

18.如何自学Struts2之Struts2标签和集成初略总结篇[视频] 之前写了一篇"打算做一个视频教程探讨如何自学计算机相关的技术",优酷上传不了,只好传到百度云上: http://pan.baidu.com/s/1kTDsa95 有问题可以直接回复这篇文章.

Cocos2dx-Android初体验

windows下android平台cocos2dx. 首先得自己具备如下eclipse(adt.cdt).cygwin.android-ndk .android-sdk,自己下载安装,不做详细解释. 一.下载cocos2dx. http://www.cocos2d-x.org/download 我的cocos2dx目录为D:\2013\cocos2dx\cocos2d-x-2.1.4\cocos2d-x-2.1.4 二.首先进行android版配置,需要修改几个地方. 1.进入目录,修改crea

Java8初体验(1):lambda表达式语法

原文出处: 一冰_天锦 本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人也是刚刚开始学习Java8,所以文中肯定有错误和理解偏差的地方,希望大家帮忙指出,我会持续修改和优化.本文是该系列的第一篇,主要介绍Java8对屌丝码农最有吸引力的一个特性—lambda表达式. java8的安装 工欲善其器必先利其器,首先安装JDK8.过程省略,大家应该都可以自己搞定.但是有一点这里强调一下(Windows系统):目前我们工作的版本一般是java 6或者java 7,所以很多人安

Spring事务传递性探讨

本篇主要讨论下面几点获取[下载地址]  : 一: Spring 事务的传递性介绍 二: 第三方调用含有事务的Service抛异常方法探讨 一: Spring 事务的传递性介绍 事务传播行为,所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为.在TransactionDefinition定义中包括了如下几个表示传播行为的常量: TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,

玩转oo对象模型(1) 之 初窥c++对象模型

============================================== copyright: KIRA-lzn ============================================== 转载请注明出处,这篇是我原创,翻版必究! ============================================== 第二篇,如果写的好,请点个赞呦.当然有不对之处,非常欢迎拍砖!!!! 自我介绍: USTC研一学生,目前在intel实习,研发岗 第二篇

SQL Server中关于基数估计如何计算预估行数的一些探讨

关于SQL Server 2014中的基数估计,官方文档Optimizing Your Query Plans with the SQL Server 2014 Cardinality Estimator里有大量细节介绍,但是全部是英文,估计也没有几个人仔细阅读.那么SQL Server 2014中基数估计的预估行数到底是怎么计算的呢? 有哪一些规律呢?我们下面通过一些例子来初略了解一下,下面测试案例仅供参考,如有不足或肤浅的地方,敬请指教! 下面实验测试的环境主要为SQL Server 201

我的博客园初篇 解密 js原型继承

网上的关于原型继承的文章多入牛毛 但是我感觉并不适合新手了解 首先假如这位新手了解过面向对象java啊 c#啊他会理解继承 但是和javascript的继承有有所不同,又假如这位新手初入前端 以前没了解过面向对象 你和他说一大堆术语 更加把他搞得蒙头转向.我在这里尝试用人类认知的模式来解释什么事原型继承以及为什么需要它. 从某种意义来说面向对象的三大特性在我看来是为了“偷懒”(纵观全世界科技发展莫不如此)而由聪明人想出来的法子,在代码世界中,代码的复用也就是多次使用时司空见惯的,比如你一进门就写