算法 | 详解斐波那契数列问题

news2024/11/20 11:24:02

14天阅读挑战赛

本篇是学习了《趣学算法(第2版)》 第一章之后总结的。
在这里插入图片描述

上一篇讲到了等比数列求和问题,求 S n = 1 + 2 + 2 2 + 2 3 + . . . + 2 63 = ? S_n = 1 + 2 + 2^2 + 2^3 + ... + 2^{63}= ? Sn=1+2+22+23+...+263=,该函数属于爆炸增量函数,如果采用常规运算,则要考虑算法的时间复杂度。

算法时间复杂度

常见的算法时间复杂度有以下几类。

  1. 常数阶。
    常数阶算法的运行次数是一个常数,如5、20、100。常数阶算法的时间复杂度通常用O(1)表示。

  2. 多项式阶。
    很多算法的时间复杂度是多项式,通常用 0(n)、 O ( n 2 ) O(n^2) O(n2) 0 ( n 3 ) 0(n^3) 0(n3)等表示。

  3. 指数阶。
    指数阶算法的运行效率极差,程序员往往像躲“恶魔”一样避开这种算法。指数阶算法的时间复杂度通常用 O ( 2 n ) O(2^n) O(2n) O ( n ! ) O(n!) O(n!) O ( n n ) O(n^n) O(nn)等表示。

  4. 对数阶。
    对数阶算法的运行效率较高,通常用 O ( l o g n ) O(logn) O(logn) O ( n l o g n ) O(nlogn) O(nlogn)等表示。
    指数阶增量随着的增加而急剧增加,而对数阶增长缓慢。它们之间的关系如下:

O ( 1 ) < O ( l o g n ) < O ( n ) < O ( n l o g n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) O ( n ! ) < O ( n n ) O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)O(n!)<O(n^n) O(1)O(logn)O(n)O(nlogn)O(n2)O(n3)O(2n)O(n!)O(nn)

在设计算法时,我们要注意算法复杂度增量的问题,尽量避免爆炸级增量。

算法知识点

  • 斐波那契数

  • 动态规划(拆分子问题;记住过往,减少重复计算)

算法题目

假设第1个月有1对初生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生
1对兔子,兔子永不死去……那么,由1对初生的兔子开始,12个月后会有多少对兔子呢?

做题思路

在这里插入图片描述

这个数列有如下十分明显的特点:从第3个月开始, 当月的兔子数 = 上月兔子数 + 当月新生兔子数 当月的兔子数=上月兔子数+当月新生兔子数 当月的兔子数=上月兔子数+当月新生兔子数,而 当月新生兔子数 = 上上月的兔子数 当月新生兔子数=上上月的兔子数 当月新生兔子数=上上月的兔子数。因此,前面相邻两项之和便构成后一项,换言之:
当月的兔子数 = 上月兔子数 + 上上月的兔子数 当月的兔子数=上月兔子数+上上月的兔子数 当月的兔子数=上月兔子数+上上月的兔子数

斐波那契数如下:

1 ,1 ,2 ,3 ,5 ,8, 13 ,21 ,34 ......

递归表达式

F ( n ) = { 1 ,n=1 1 ,n=2 F ( n − 1 ) + F ( n − 2 ) ,n>2 F(n)= \begin{cases} 1&, \text{n=1}\\ 1&, \text{n=2}\\ F(n-1) + F(n-2)&, \text{n>2} \end{cases} F(n)= 11F(n1)+F(n2)n=1n=2n>2

根据递归表达式,初步的算法代码如下:

const fbn = (n) => {
  if (n == 1 || n == 2) {
		return 1
  } else {
		return fbn(n-2) + fbn(n-1)
	}
}

让我们看一下上面算法的时间复杂度,也就是计算的总次数 T ( n ) T(n) T(n)

时间复杂度

时间复杂度算的是最坏情况下的时间复杂度

n=1时,T(n)=1
n=2时,T(n)=1;
n=3时,T(n)=3; //调用Fib1(2)和Fib1(1)并执行一次加法运算(Fib1(2)+Fib1(1))

当n>2时需要分别调用fbn(n-1)fbn(n-2),并执行一次加法运算,换言之:
n > 2 时, T ( n ) = T ( n − 1 ) + T ( n − 2 ) + 1 ; n\gt2时,T(n)=T(n-1)+T(n-2)+1; n>2时,T(n)=T(n1)+T(n2)+1;

所以, T ( n ) > = F ( n ) T(n) >= F(n) T(n)>=F(n)

问题来了,怎么判断T(n)属于算法时间复杂度的哪种类型呢?

方法一:

画出递归树,每个节点表示计算一次

在这里插入图片描述

一棵满二叉树,节点总数就和树的高度指数关系

递归树 F(n)里面存在满二叉树,所以时间复杂度是指数阶的

方法二:

使用公式进行递推
在这里插入图片描述
因为时间复杂度算的是最坏情况下的时间复杂度,所以计算第一个括号内的即可

即: T ( n ) = O ( 2 n ) T(n) = O(2^n) T(n)=O(2n),时间复杂度是指数阶

算法改进

降低时间复杂度

不难发现:上面基于递归表达式的算法,存在大量的重复计算,增大了算法的时间复杂度,所以我们可以做出如下改进,以减少时间复杂度

// 利用数组记录过往的值,直接使用,避免重复计算
const fbn2 = (n) => {
  let arr = new Array(n + 1); // 定义 n + 1 长度的数组
  arr[1] = 1;
  arr[2] = 1;
  for (let i = 3; i <= n; i++) {
    arr[i] = arr[i - 1] + arr[i - 2]
  }
  return arr[n]
}

很显然上面算法的时间复杂度是 O ( n ) O(n) O(n),时间复杂度从指数阶降到了多项式阶。

由于上面算法使用数组记录了所有项的值,所以,算法的空间复杂度变成了 O ( n ) O(n) O(n),我们可以继续改进算法,来降低算法的空间复杂度

降低空间复杂度

采用临时变量,来迭代记录上一步计算出来的值,代码如下:

const fbn3 = (n) => {
  if (n === 1 || n === 2) {
    return 1;
  }
  let pre1 = 1 // pre1,pre2记录前面两项
  let pre2 = 1
  let tmp = ''

  for (let i = 3; i <= n; i++) {
    tmp = pre1 + pre2 // 2
    pre1 = pre2 // 1
    pre2 = tmp // 2
  }
  return pre2
}

使用了三个辅助变量,时间复杂度还是 O ( n ) O(n) O(n),空间复杂度降为 O ( 1 ) O(1) O(1)

测试算法计算时间

// 斐波那契数列
// 1 ,1 ,2 ,3 ,5 ,8, 13 ,21 ,34 ......

const fbn = (n) => {
  if (n == 1 || n == 2) {
		return 1
  } else {
		return fbn(n-2) + fbn(n-1)
	}
}
console.time('fbn')
console.log('fbn(40)=', fbn(40))
console.timeEnd('fbn')

// 利用数组记录过往的值,直接使用,避免重复计算
const fbn2 = (n) => {
  let arr = new Array(n + 1); // 定义 n + 1 长度的数组
  arr[1] = 1;
  arr[2] = 1;
  for (let i = 3; i <= n; i++) {
    arr[i] = arr[i - 1] + arr[i - 2]
  }
  return arr[n]
}

console.time('fbn2')
console.log('fbn2(40)=', fbn2(40))
console.timeEnd('fbn2')

const fbn3 = (n) => {
  if (n === 1 || n === 2) {
    return 1;
  }
  let pre1 = 1 // pre1,pre2记录前面两项
  let pre2 = 1
  let tmp = ''

  for (let i = 3; i <= n; i++) {
    tmp = pre1 + pre2 // 2
    pre1 = pre2 // 1
    pre2 = tmp // 2
  }
  return pre2
}

console.time('fbn3')
console.log('fbn3(40)=', fbn3(40))
console.timeEnd('fbn3')

测试结果如下:

fbn(40)= 102334155
fbn: 667.76ms
fbn2(40)= 102334155
fbn2: 0.105ms
fbn3(40)= 102334155
fbn3: 0.072ms

小结

能不能继续降阶,使算法的时间复杂度更低呢?
实质上,斐波那契数列的时间复杂度还可以降到对数阶 O ( l o g n ) O(logn) O(logn),好厉害!!!后面继续探索吧


我是 甜点cc

热爱前端,也喜欢专研各种跟本职工作关系不大的技术,技术、产品兴趣广泛且浓厚,等待着一个创业机会。本号主要致力于分享个人经验总结,希望可以给一小部分人一些微小帮助。

希望能和大家一起努力营造一个良好的学习氛围,为了个人和家庭、为了我国的互联网物联网技术、数字化转型、数字经济发展做一点点贡献。数风流人物还看中国、看今朝、看你我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1024,我们节日

文章目录1024的前奏1024应该锻炼身体1024 &#xff1f;为何是1024其他的1024代码有Bug&#xff0c;世界却温暖特殊的一类人群&#xff08;一&#xff09;特殊的一类人群&#xff08;二&#xff09;写在最后1024的前奏 写博文的时间是23号&#xff0c;距离1024这个标志性的节日…

趣学算法14天阅读|Day2

14天阅读挑战赛 文章目录前言什么是算法&#xff1f;算法复杂度如何评定好算法案例案例一&#xff1a;棋盘的麦子案例二&#xff1a;兔子数列总结前言 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端中级工…

@AutoConfigurationPackage注解详解

AutoConfigurationPackage是在springboot启动类注解SpringBootApplication下的EnableAutoConfiguration下。AutoConfigurationPackage作用是指定springboot扫描包&#xff0c;默认就是扫描启动类同包下的类。可以通过AutoConfigurationPackage来附加其他路径&#xff0c;然后sp…

asp.net园林绿化服务交易网站

本设计的目的是通过对园林绿化的初步了解&#xff0c;在巩固网站设计所学知识的基础上。运用于实践&#xff0c;在实践中强化理论知识&#xff0c;做到理论联系实践。在设计中学会搜集资料地方法&#xff0c;学会查阅设计规程。掌握设计步骤&#xff0c;同时培养创新能力&#…

【图像评价】基于matlab GUI图像评价系统【含Matlab源码 2185期】

一、图像质量评价 图像质量评价对图像处理算法的可行性分析以及某类算法达成效果的比较都起着非常重要的指导作用。随着图像处理技术的发展&#xff0c;对于图像质量评价的研究也日益深入&#xff0c;新的评价指标和评价算法不断涌现&#xff0c;共同推动了图像质量评价的进步…

【模型训练】YOLOv7旋翼无人机检测

YOLOv7旋翼无人机检测 1、YOLOv7算法旋翼无人机检测模型训练2、YOLOv7模型模型评估3、模型和数据集下载1、本项目采用YOLOv7算法实现对空中旋翼无人机的检测,在一万多张旋翼无人机检测数据集中训练得到,我们训练了YOLOv7模型,经评估我们得出了各个模型的评价指标; 2、目标类…

研发效能工程实践开篇

背景 随着技术的发展&#xff0c;大至一线互联网公司、小至几十人研发团队的公司都开始开展devops运动。大公司有能力自研&#xff0c;小公司则采用开源解决方案。如何实施devops业界已经有非常成熟的方案&#xff0c;所以本系列不再谈devops工具链以及如何实施。本系列想谈谈…

初识C++ - 类与对象(中篇·下半)

目录 赋值运算符重载 以下是一个日期类的运算符重载的案例(重点) 关于流插入与流提取的使用 方法一&#xff1a;定义与声明分离 方法二&#xff1a;使用内联函数 const成员 概念 关于上述日期类代码为什么需要在函数后面加入const 取地址及const取地址操作符重载 …

5G核心网技术基础自学系列 | 网络切片

书籍来源&#xff1a;《5G核心网 赋能数字化时代》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G核心网技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 支持网络切片是设计5G架构的主要驱…

【牛客刷题--SQL篇】必会的常用函数之条件函数SQL26计算25岁以上和以下的用户数量(多种写法)

&#x1f496;个人主页&#xff1a;与自己作战 &#x1f4af;作者简介&#xff1a;CSDN博客专家、CSDN大数据领域优质创作者、CSDN内容合伙人、阿里云专家博主 &#x1f49e;牛客刷题系列篇&#xff1a;【SQL篇】】【Python篇】【Java篇】 &#x1f4cc;推荐刷题网站注册地址&a…

基于WEB的二维码生成系统设计与实现(Asp.net)

目 录 1 引言 6 2 二维码的生成原理 6 2.1基础知识 6 2.1.1定位图案 7 2.1.2功能性数据 8 2.1.3数据码和纠错码 8 2.2数据编码 8 2.3结束符和补齐符 9 2.3.1补齐码&#xff08;Padding Bytes&#xff09; 9 2.3.2纠错码 10 2.4最终编码 11 2.4.1穿插放置 11 2.4.2Remainder Bi…

JWT基础概念

1. 介绍 JWT &#xff08;JSON Web Token&#xff09; 是目前最流行的跨域认证解决方案&#xff0c;是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出&#xff0c;JWT 本身也是 Token&#xff0c;一种规范化之后的 JSON 结构的 Token。 JWT 自身包含了身份验证所需要…

【正点原子I.MX6U-MINI应用篇】6、嵌入式Linux在LCD屏幕上显示字符

一、原始方式&#xff1a; 取模显示字符 LCD显示屏是由width * height个像素点构成的&#xff0c;显示字符&#xff0c;一个非常容易想到的方法便是对字符取模&#xff0c;然后在LCD屏上打点显示字符&#xff1b;如果大家以前学习过单片机&#xff0c;想必接触过一些显示屏&am…

三次、五次多项式插值(附代码)

文章目录一、三次多项式插值二、五次多项式插值三、matlab代码三次、五次多项式插值在工程实践中很常见。求解多项式的系数最直接的方法是根据端点处的约束条件&#xff0c;列出线性方程组&#xff0c;再写成矩阵方程AXB&#xff0c;然后用通用的方法(如高斯消元法、LU分解等)解…

二叉树广度优先搜索、深度优先搜索(前序、中序、后序)遍历,动图详解-Java/Kotlin双版本代码

自古逢秋悲寂寥&#xff0c;我言秋日胜春朝 二叉树结构说明 本博客使用树节点结构&#xff0c;如下所示&#xff1a; Kotlin 版本 class TreeNode(var value: String, var leftNode: TreeNode? null, var rightNode: TreeNode? null)Java 版本 class TreeNode(){public…

经典排序之插入排序

目录 直接插入排序&#xff1a; 基本思路 图解过程 代码 复杂度分析 希尔排序 基本思想 图解过程 代码 复杂度分析 总结 参赛话题&#xff1a;学习笔记 直接插入排序&#xff1a; 基本思路 直接插入排序的工作方式像许多人排序一手扑克牌。开始时&#xff0c;我们的左手…

【Netty 从成神到升仙系列 大结局】全网一图流死磕解析 Netty 源码

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;Java领域新星创作者。&#x1f4dd;个人公众号&#xff1a;爱敲代码的小黄&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法&#x…

第八篇 python 面向对象编程

11 面向对象编程 面向对象编程——Object Oriented Programming&#xff0c;简称OOP&#xff0c;是一种程序设计思想。OOP把对象作为程序的基本单元&#xff0c;一个对象包含了数据和操作数据的函数。 面向过程的程序设计把计算机程序视为一系列的命令集合&#xff0c;即一组…

Python攻防-APK批量化Pull与自动化反编译

文章目录前言Pull APK根据包名列表根据手机路径逆向APK自动化反编译findstr检索…总结前言 日常工作过程中&#xff0c;经常会遇到发现新的攻击模式的情况下&#xff0c;需要全量排查手机上所有 APP 的代码是否存在该类代码缺陷。对于复杂的攻击模式而言&#xff0c;往往需要动…

【MyBatis框架】动态SQL

MyBatis之动态SQL 目录MyBatis之动态SQL1. < if > 元素2. < where >3. < choose >,< when >,< otherwise >元素4. < trim >元素5. < set >元素6. < foreach >元素6.1 添加批量数据6.2 批量删除数据7. < SQL >元素8. 小结…