使用递归的注意事项和陷阱 | 学习笔记

简介: 快速学习使用递归的注意事项和陷阱

开发者学堂课程【Scala 核心编程 - 进阶使用递归的注意事项和陷阱学习笔记,与课程紧密连接,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/610/detail/9101


使用递归的注意事项和陷阱


使用递归的注意事项

以求斐波拉契为例,来讲递归的注意事项是什么,先看递归求斐波拉契代码应该怎么写,叫 RecursiverFnb ,斐波那契是一个很厉害的人,为了纪念他将它的序列命令为斐波那契数列,学习斐波拉契数列直接写一个函数,写 fnb ,然后写一个(  n :bigint )表示将来会很大,但返回也是Bigint ,如果 n=1或 n=2则返回1,若 else 则fbn ( n-1 )+ fbn ( n-2)。

接下来会出现什么现象呢,当传一个3进去,斐波拉契系数应该是1,1,2 ,3,5,以此类推,如果给一个3,返回一个2,没有问题,速度很快就出来了,再传一个10,速度也会很快55。

我们来研究一下,当我们使用斐波拉契阶层来处理求值时,观察递归的次数是如何增长的。

研究递归求斐波拉契的数的递归次数的增长情况。如现在整一个数,来把这个数传进去,把这个函数拎到里面,传一个全局变量去测试,先在这里面写一个非常大的数,var count =  Bigint ,初始化为0。此时,调用一次加1,调用递归的次数是把它加起来。

如果传3,递归次数可以看出是3,没问题,如果执行四次,调用次数会变成5,也很正常,五次时,会发现次数开始越来越快,当六时,递归次数变成15次,直接20次,递归一共是13529,求10时,递归次数为13529,求11时,只长了一个,调用次数的增长特恐怖,已经两万次了,因为这个地方要思考11过后里面会重复计算,增加了1,就变很多,几乎是从倍数增长,当30次时,会按照一两千万次的次数增长,30时,是160万好,当31时,200多万,当40次时,程序已经跑不起来了,代码会卡。

递归就不能这么用,由此可得出递归的次数呈现指数增长,增长的根本原因在于里面有两次调用。

如果不是有两次调用,而只是一次就不会指数增长。测一下求阶层,它的统计次数的增长只会增加一个,非常有效。求阶乘和这个最大区别在哪里?

假设不用斐波拉契,换用数测试时,降为了20次,若为40,应当为几千万次的调用,而这里50,只调用25次。因此在写递归时,要考虑出现重复计算,此时要考虑优化,优化的原则一般是改递归为迭代或使用其他办法减少递归次数。

package com.atguigu.chapter14

object RecursiveFnb {

def main(args:Array[string]):Unit={

var count =BigInt(0)

//1 1 2 3 5 ?

println(fbn(50)) // 10 -> 13529 11->21891

println("递归的次数是="+ count)

//研究下递归求斐波拉契的数的递归次数的增长情况!

//递归的次数是指数增长.

def fbn(n:BigInt): BigInt = {

count += 1

if(n==1|n==2)1

else fbn(n-1)+fbn(n-2)//重复计算,需要考虑优化->选代

}

}

有一篇文专门讲了对斐波拉契的优化,一共有多种方式,这里面简单了解。

https://blog.csdn.net/dadai/article/details/50209511

第一次直接改迭代,没有用递归,没有用递归了。

下面继续优化,递归调用,但是优化较好。

越到后面越不容易看懂,是迭代加递归结合使用,这里不是讲算法,讲完课程过后会专门讲数据算法,这里涉及到很多优化的方案,有些需要数学功底的。

这里主要强调递归时防止重复计算,如前面求最大值,求反转的写法绝对不会出问题,因为只有一次,求阶乘,速度也会很快,因为次数并不多,如500啊,BigInt可以算出的,它底层用了不是简单的一个数的存放,是字符串进行处理。如改为 n-2 ,就有重复计算,30时就无法计算。

package com.atguigu.chapter14

object RecursiveFactoria {

def main(args: Array[String]): Unit ={

println(factorial(30)) // 1*2*...10 =

}

//求出阶乘

def factorial(n: BigInt): BigInt =

if (n == 0) 1 else factorial(n - 2) * factorial(n - 1)

}

对于这样的问题,有一种学科专门来研究代码,它的执行的效率以及它递归的次数,叫数值分析,就是专门研究算法的程序必学的一门课程,专门研究代码的执行的效率高低,两个代码不用执行,就可通过数字分析来判断效率高低,因为主要是观察调用的次数,能计算出按什么样的规律来增长。

使用递归的注意事项,在开发时要注意,否则代码会出大问题。

相关文章
|
2月前
|
算法
‘/’ 和 ‘%’ 在编程中的作用【附加练习题】
‘/’ 和 ‘%’ 在编程中的作用【附加练习题】
|
19天前
|
机器学习/深度学习 算法 C语言
详细介绍递归算法在 C 语言中的应用,包括递归的基本概念、特点、实现方法以及实际应用案例
【6月更文挑战第15天】递归算法在C语言中是强大力量的体现,通过函数调用自身解决复杂问题。递归涉及基本概念如自调用、终止条件及栈空间管理。在C中实现递归需定义递归函数,分解问题并设定停止条件。阶乘和斐波那契数列是经典应用示例,展示了递归的优雅与效率。然而,递归可能导致栈溢出,需注意优化。学习递归深化了对“分而治之”策略的理解。**
32 7
|
19天前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
|
2月前
|
算法 C语言 C++
【新手解答8】深入探索 C 语言:递归与循环的应用
【新手解答8】深入探索 C 语言:递归与循环的应用
44 0
|
2月前
|
机器学习/深度学习 算法 C语言
【新手解答9】深入探索 C 语言:递归与循环的应用2
【新手解答9】深入探索 C 语言:递归与循环的应用2
52 0
|
11月前
|
运维 Shell 数据安全/隐私保护
【运维知识高级篇】超详细的Shell编程讲解4(for循环+并发问题+while循环+流程控制语句+函数传参+函数变量+函数返回值+反向破解MD5)(一)
【运维知识高级篇】超详细的Shell编程讲解4(for循环+并发问题+while循环+流程控制语句+函数传参+函数变量+函数返回值+反向破解MD5)
145 0
|
11月前
|
运维 Shell
【运维知识高级篇】超详细的Shell编程讲解4(for循环+并发问题+while循环+流程控制语句+函数传参+函数变量+函数返回值+反向破解MD5)(二)
【运维知识高级篇】超详细的Shell编程讲解4(for循环+并发问题+while循环+流程控制语句+函数传参+函数变量+函数返回值+反向破解MD5)(二)
96 0
|
11月前
如何使用递归及注意事项
如何使用递归及注意事项
56 0
|
存储 编译器 C语言
【C】函数真的难嘛?其实一点也不难,原理很简单。
# 什么是函数 程序是由多个零件组合而成的,而函数就是这种“零件”的一个较小单位。 ## main函数和库函数 C语言程序中,main函数是必不可少的。程序运行的时候,会执行main函数的主题部分。main函数中使用了printf、scanf、puts等函数。由C语言提供的这些为数众多的函数称为库函数。 ## 什么是函数 当然,我们也可以自己创建函数。而实际上,我们也必须亲自动手创建各种函数。下面我们来自己创建一个简单的函数。 创建一个函数,接收两个整数参数,返回较大整数的值。 printf函数和scanf函数等创建得比较好得函数,即使不知道其内容,只要了解使用方法,也可以轻松使用。 ## 函
feof用法重点详解(易被误用判断文件结束!!!)
feof用法重点详解(易被误用判断文件结束!!!)