尾递归和一般的递归不同在对内存的占用,普通递归创建stack累积而后计算收缩,尾递归只会占用恒量的内存(和迭代一样)。SICP中描述了一个内存占用曲线,用以上答案中的Python代码为例(普通递归):

def recsum(x):
if x == 1:
return x
else:
return x + recsum(x - 1)

当调用recsum(5),Python调试器中发生如下状况:

recsum(5)
5 + recsum(4)
5 + (4 + recsum(3))
5 + (4 + (3 + recsum(2)))
5 + (4 + (3 + (2 + recsum(1))))
5 + (4 + (3 + (2 + 1)))
5 + (4 + (3 + 3))
5 + (4 + 6)
5 + 10
15

这个曲线就代表内存占用大小的峰值,从左到右,达到顶峰,再从右到左收缩。而我们通常不希望这样的事情发生,所以使用迭代,只占据常量stack space(更新这个栈!而非扩展他)。
---------------------
(一个替代方案:迭代
for i in range(6):
sum += i
因为Python,Java,Pascal等等无法在语言中实现尾递归优化(Tail Call Optimization, TCO),所以采用了for, while, goto等特殊结构代替recursive的表述。Scheme则不需要这样曲折地表达,一旦写成尾递归形式,就可以进行尾递归优化。
---------------------
Python中可以写(尾递归):
def tailrecsum(x, running_total=0):
if x == 0:
return running_total
else:
return tailrecsum(x - 1, running_total + x)
理论上类似上面:
tailrecsum(5, 0)
tailrecsum(4, 5)
tailrecsum(3, 9)
tailrecsum(2, 12)
tailrecsum(1, 14)
tailrecsum(0, 15)
15
观察到,tailrecsum(x, y)中形式变量y的实际变量值是不断更新的,对比普通递归就很清楚,后者每个recsum()调用中y值不变,仅在层级上加深。所以,尾递归是把变化的参数传递给递归函数的变量了
怎么写尾递归?形式上只要最后一个return语句是单纯函数就可以。如:
return tailrec(x+1);
return tailrec(x+1) + x;
则不可以。因为无法更新tailrec()函数内的实际变量,只是新建一个栈。

但Python不能尾递归优化(Java不行,C可以,我不知道为什么),这里是用它做个例子。
==================================== 如何优化尾递归
在编译器处理过程中生成中间代码(通常是三地址代码),用编译器优化。

【algorithm】尾递归的更多相关文章

  1. 挑子学习笔记:两步聚类算法(TwoStep Cluster Algorithm)——改进的BIRCH算法

    转载请标明出处:http://www.cnblogs.com/tiaozistudy/p/twostep_cluster_algorithm.html 两步聚类算法是在SPSS Modeler中使用的 ...

  2. PE Checksum Algorithm的较简实现

    这篇BLOG是我很早以前写的,因为现在搬移到CNBLOGS了,经过整理后重新发出来. 工作之前的几年一直都在搞计算机安全/病毒相关的东西(纯学习,不作恶),其中PE文件格式是必须知识.有些PE文件,比 ...

  3. [异常解决] windows用SSH和linux同步文件&linux开启SSH&ssh client 报 algorithm negotiation failed的解决方法之一

    1.安装.配置与启动 SSH分客户端openssh-client和openssh-server 如果你只是想登陆别的机器的SSH只需要安装openssh-client(ubuntu有默认安装,如果没有 ...

  4. [Algorithm] 使用SimHash进行海量文本去重

    在之前的两篇博文分别介绍了常用的hash方法([Data Structure & Algorithm] Hash那点事儿)以及局部敏感hash算法([Algorithm] 局部敏感哈希算法(L ...

  5. Backtracking algorithm: rat in maze

    Sept. 10, 2015 Study again the back tracking algorithm using recursive solution, rat in maze, a clas ...

  6. [Algorithm & NLP] 文本深度表示模型——word2vec&doc2vec词向量模型

    深度学习掀开了机器学习的新篇章,目前深度学习应用于图像和语音已经产生了突破性的研究进展.深度学习一直被人们推崇为一种类似于人脑结构的人工智能算法,那为什么深度学习在语义分析领域仍然没有实质性的进展呢? ...

  7. [Algorithm] 群体智能优化算法之粒子群优化算法

    同进化算法(见博客<[Evolutionary Algorithm] 进化算法简介>,进化算法是受生物进化机制启发而产生的一系列算法)和人工神经网络算法(Neural Networks,简 ...

  8. [Evolutionary Algorithm] 进化算法简介

    进化算法,也被成为是演化算法(evolutionary algorithms,简称EAs),它不是一个具体的算法,而是一个“算法簇”.进化算法的产生的灵感借鉴了大自然中生物的进化操作,它一般包括基因编 ...

  9. 尽量用goto代替尾递归

    void PrintList(List L) { if(L!=Null) { PrintElement(L->Element); PrintLisr(L->Next); } } 所谓尾递归 ...

随机推荐

  1. 【lua实战摸索】在b.lua调用a.lua的函数

    需要掌握知识: lua table的使用(创建自己函数的表作为函数库) 普通函数的调用:tab.func(tab,参数) 等效于表中函数的调用tab:func(参数) 基本思路: 1.在相同目录下创建 ...

  2. python3写冒泡排序

    1.概念理解: 冒泡排序:可以简单的理解为是列表中相近的元素,两两比较,小的在前面.最多需要len()-1次排序. 2.例子:a=[11,7,4,56,35,0] 3.代码实现: 4.输出结果: 第1 ...

  3. C语言中函数参数传递的本质是值传递

    数组名做函数参数进行传递时,实际上是是一份该指针的拷贝. 给形参赋予其他值,并不影响实参的值. 类似于: int *p = a;    //a为数组名 p = b;          //b为数组名 ...

  4. WebGL 绘制Line的bug(二)

    上一篇文章简单介绍了WebGL绘制Line的bug,不少朋友给我发了私信,看来这个问题大家都遇上过哈.今天这篇文章会讲述解决这个问题的work around. 基本思路 上一篇文章结尾简单提了下解决的 ...

  5. ç7—UIViewController

    UIViewController继承了UIResponder,而UIResponder继承了NSObject,UIViewController是所有视图控制器的父类. 在MVC模式中,UIViewCo ...

  6. tabs标签页的数据缓存

    一进入tabs标签页默认就将所有标签页的数据请求到,并渲染到页面上, 这样如果数据量太大的话会渲染很久, 我的需求就是点击不同的标签时再请求数据,同时对点击过的标签页数据进行缓存,下次点击时不再重新请 ...

  7. FFT NTT 模板

    NTT: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; # ...

  8. ubuntu mysql配置方案

    Ubuntu常用服务器环境搭建--MySQL篇 MySQL 1.安装MySQL apt-get update apt-get install mysql-server 2.配置MySQL vi /et ...

  9. [工具]Visual Studio

    1,Tab键的使用: 如不说有这样的代码:public Member member { get; set; } 当我们编辑完Member后,按一下Tab键,就能够将光标锁定到member上,等待键盘输 ...

  10. UI进阶 XML解析适配 'libxml/tree.h'file not found 错误解决办法

    Xcode 'libxml/tree.h'file not found 错误解决办法