当前位置: 首页 > news >正文

斐波那契数列的递归和迭代实现

一、斐波那契数列的数学定义

斐波那契数列(Fibonacci Sequence)的定义具有明确的递推关系,是算法实现的核心依据:

  • 初始条件:F (0) = 0,F (1) = 1(注:部分场景以 F (1)=1,F (2)=1 为初始项,需根据业务需求统一标准,本文以 F (0) 为起始索引)
  • 递推公式:对任意整数 n ≥ 2,F (n) = F (n-1) + F (n-2)

例如斐波那契数列的前10项为1,1,2,3,5,8,13,21,34,55

二、递归算法

递归算法的核心是 “将大问题拆解为同类小问题”,通过调用自身求解子问题,最终聚合得到原问题答案。这种实现方式与斐波那契数列的递推公式高度契合,代码简洁但需关注效率问题。

2.1 核心逻辑

递归实现的关键在于明确 “基线条件” 与 “递归拆解”

基线条件:当 n=1 或 n=2 时,直接返回初始值1.

递归拆解:当 n>2 时,将 F (n) 拆解为 F (n-1) 和 F (n-2) 两个子问题,通过递归调用获取子问题结果后相加,得到 F (n) 的值.

2.2 C++实现

long fibi(int n) {//递归算法if (n == 1 || n == 2)return 1;//基线条件return fibi(n - 1) + fibi(n - 2);//递归拆解
}

注意这里取用long类型,即64位有符号整数,其取值最大为2^63-1,因此最多只能算到斐波那契数列的第46位.当需要计算的位数极多时(如计算fibi(100)),可考虑使用可以保存更多位数的数据类型,而当内置数据类型也不满足需求时,可考虑自定义类型来保存大数字,比如利用数组来保存大数字的各个位上的数字,也可寻求第三方库的帮助

2.3 时间复杂度与空间复杂度分析

2.3.1 时间复杂度

递归算法的时间复杂度需分析 “总计算次数”.

总调用次数 = 各层节点数之和,近似为等比数列求和:
1+2+4+...+2^(n-1)=2^n

因此,时间复杂度为 O (2ⁿ)——指数级复杂度.当 n=30 时,调用次数约 100 万;n=40 时约 10 亿;n=50 时已超 1000 亿,完全无法实用。

显然,该算法效率极低,主要原因是大量的重复计算.例如计算fibi(20)时,fibi(2)被计算4181次,fibi(3)被计算2584次

2.3.2 空间复杂度

空间复杂度衡量算法[运行时占用的额外空间],该算法的空间消耗主要来自函数调用栈(存储每次递归调用的上下文,如参数、返回地址等)

递归算法的调用栈深度=[递归的最大层数]. 对于斐波那契递归:

  • 计算fibi(n)时,递归会先一路拆解到fibi(1),此时调用栈的深度为n.
  • 后续回溯计算fibi(n-2)时,栈会逐步弹出上层调用,栈深度不会超过n.

因调用栈深度为n,且每层调用仅占用常数级空间,故空间复杂度为O(n).

注意,当n较大时,可能会触发"栈溢出"错误.

三、迭代算法

迭代算法通过循环结构,从初始项开始逐步计算中间结果,直至得到目标值.这种实现方式避免了重复计算和栈空间占用,效率远高于普通递归

3.1 核心逻辑

迭代实现的关键在于"状态保存"与"循环推进":​

状态初始化:定义两个变量保存前两项结果​

循环推进:从fibi(3)开始,每次先更新past和prev为上一轮循环结束后curr的前两项,再通过past和prev计算当前项(curr=past+prev)​

终止条件:当执行n-2次循环后,得到目标值

3.2 C++实现

long fibr(int n) {//递归算法long past, prev, curr;//临时变量分别存储第k-2,k-1,k项的值(k为循环次数+2)past = prev = curr = 1;//初始化for(int i=3;i<=n;i++){//不断递归计算下一项值past = prev;//存储fibr(i-2)prev = curr;//存储fibr(i-1)curr = past + prev;//存储fibr(i)}return curr;
}

3.3 时间复杂度和空间复杂度分析

3.3.1 时间复杂度

迭代算法的时间复杂度由"循环次数"决定.

该算法执行n-2次循环,原因:
第一次for循环,目标为fibr(3).past表示fibr(1),prev表示fibr(2),curr由两者相加,表示fibr(3);
第二次for循环,目标为fibr(4).past表示fibr(2),prev表示fibr(3),curr由两者相加,表示fibr(4);
第k次for循环将会得到fibr(k+2)的值.past是fibr(k+2)前二位,prev是fibr(k+2)前一位,curr由这两位相加得到fibr(k+2)的值
进入下一次循环,目标为fibr(k+3).此时past利用prev保持与目标值的相对位置,prev由curr保持与目标值的相对位置,最终curr由两值相加得到目标值
令k+2=n,则k=n-2,即需要进行n-2次循环即可得到目标值.

因此时间复杂度为 O (n),即线性级时间复杂度

3.3.2 空间复杂度

迭代算法仅使用变量复用保存状态,无论n多大,变量数量始终不变,因此空间复杂度为O(1),即常数级空间复杂度

四、总结

斐波那契数列的两种基础实现——递归与迭代,分别代表了"分治"与"递推"两种算法思想:

  • 递归实现比迭代实现慢得多的原因:递归频繁调用栈,且有大量重复计算;而迭代仅用循环和变量复用实现
  • 递归的优势在于代码简洁,可读性高,但其指数级时间复杂度和栈溢出风险限制了实际应用
  • 迭代通过状态保存与循环推进,实现了O(n)时间复杂度和O(1)空间复杂度,性能较好

http://www.dtcms.com/a/389867.html

相关文章:

  • 时空预测论文分享:规则知识 因果预测框架 面向研究的评估体系 主动适应漂移
  • 《WINDOWS 环境下32位汇编语言程序设计》第18章 ODBC数据库编程
  • linux入门(3)
  • 任意版本GitLens vscode插件破解邪修秘法
  • Redis最佳实践——热点数据缓存详解
  • font简写和CSS的继承性
  • 高性能服务器配置经验指南6——BIT校园网在ubuntu中的自动检查连接状况脚本使用
  • SQL 连接详解:内连接、左连接与右连接
  • C2000基础-TIM介绍及使用
  • Day 06 动作类的初始化类------以B1为例
  • 面试题:对数据库如何进行优化?
  • samurai 点选分割 box分割
  • 计算机架构的总线协议中的等待状态是什么?
  • C++:入门基础(1)
  • ACD智能分配:服务延续和专属客服设置
  • 自监督学习分割
  • 抛弃自定义模态框:原生Dialog的实力
  • LangGraph 简单入门介绍
  • Docker 部署 DzzOffice:服务器 IP 转发功能是否需要开启
  • 无人机避障——卡内基梅隆大学(CMU)CERLAB 无人机自主框架复现
  • 正点原子zynq_FPGA-初识ZYNQ
  • Vue3中对比ref,reactive,shallowRef,shallowReactive
  • 通过Freemark渲染数据到Word里并生成压缩包
  • Vue 项目中使用 AbortController:解决请求取消、超时与内存泄漏问题
  • 设置管家婆服务器开机自动启动
  • ubuntu20 安装 ros2 foxy
  • 二分查找(二分查找算法)
  • 贪心算法应用:超图匹配问题详解
  • Hadoop3.3.5搭建指南(双NN版本)
  • 如何正确写Controller?参数校验、异常处理