递归函数学习 part1

news2024/12/25 10:37:34

一,初始递归:阶乘

1,原理

n的阶乘等于n乘以n-1的阶乘,而0的阶乘等于1.

2,代码展示

#include <iostream>
using namespace std;

int fact(int);
int main()
{
   cout<<fact(5);
	return 0;
}

int fact(int n)
{
	if(n==0) return 1;
	
	return n * fact(n-1);
}

二,深化理解:斐波那契数列

1,原理

当n小于等于2时,f(n)= 1;当大于2时,f(n) = f (n-1) + f(n-2).

2,代码展示

a,暴力复杂的方法,复杂度成几何形式增长

#include<iostream>

using namespace std;

int Fibonacci(int);

int main()
{
	cout << Fibonacci(30);
	return 0;
}

int Fibonacci(int n)
{
	if (n == 1 || n == 2) return 1;

	return Fibonacci(n - 1) + Fibonacci(n - 2);
}

我的电脑在n 等于100时,就半天跑不出来结果了。

b,动态规划

直接使用数组,不使用函数递归。

int Fibonacci(int n)
{
	if (n == 1 || n == 2) return 1;
	int* dp = new int[n + 1];
	dp[0] = 0;
	dp[1] = 1;

	if (n == 1 || n == 0) return 1;

	for (int i = 2; i < n + 1; i++)
	{
		dp[i] = dp[i - 1] + dp[i - 2];
	}

	int result = dp[n];
	delete[] dp;
	return result;

}

注意点:

1,动态规划设置数组大小

在c语言里你不能用dp[n]来声明一个大小为n的数组。因为数组的大小是在程序运行前就已经确定好了的,所以只能手动设置大小。有两种方法:new 和malloc。

2,数组大小设置为n+1

在斐波那契数列的动态规划实现中,你必须声明一个大小为 n+1 的数组,而不能直接声明大小为 n 的数组。原因如下:

1). 数组下标从 0 开始

数组的下标是从 0 开始的,因此如果你声明一个大小为 n 的数组,它的有效下标范围是 0 到 n-1,没有包含 n 这个位置。而为了存储第 n 个斐波那契数,你需要一个包含 dp[n] 的数组元素。因此,数组的大小应该为 n+1,以确保能够存储到 dp[n]

2). 存储斐波那契数列的所有元素

在动态规划中,你需要存储从 F(0) 到 F(n) 的所有值,以便逐步计算每个值。如果你只分配了 n 个位置,那么 dp[n] 这一位置会超出数组的范围,导致访问越界错误。

举例:

假设 n = 5,你需要存储 dp[0] 到 dp[5],总共 6 个元素。如果你只分配了一个大小为 n = 5 的数组,那么该数组的有效下标是 0, 1, 2, 3, 4,而你需要 dp[5],这就会访问越界,从而导致错误。

3,结果溢出

如果最后计算出来的结果超过了 int 类型所能表示的范围,通常会出现 整数溢出(integer overflow)。在这种情况下,计算结果会“回绕”,导致存储在 int 变量中的结果变成一个非常大的负数。这是因为 int 类型有一个固定的取值范围,如果超出了这个范围,它会从最小值重新开始,表现为负数。

int 类型的范围

在大多数平台(例如,32 位系统)上,int 类型的取值范围通常是:

  • 最小值:-2,147,483,648 (即 −231−231)
  • 最大值:2,147,483,647 (即 231−1231−1)

如果计算结果超出了这个范围,C 语言没有内建的检查机制来防止溢出,而是继续执行,导致溢出行为,并将结果存储在 int 类型的范围内。

举个例子:

假设你的系统上 int 的范围是 -2,147,483,648 到 2,147,483,647。如果你计算一个很大的斐波那契数(例如第 50 个斐波那契数,12586269025),它明显超出了 int 的范围,结果会变成负数,导致输出类似于 -2147483647 或其他类似的负数值。

为什么会显示负数?

如果结果超出了 int 能存储的最大值,计算机会“回绕”到最小值,继续在负数范围内计算,最终显示的是一个负数。实际的计算过程如下:

  1. 计算出一个超出范围的值。
  2. 由于 int 的最大范围为 2,147,483,647,当结果大于这个值时,数值会“回绕”到负数范围,得到一个负值。

c,空间优化的动态规划

1,原理

类似链表更新节点,我们可以发现求斐波那契值只涉及到前面两个数字,所以使用三个局部变量来实现更新。

2,代码展示
int Fibonacci(int n)
{
	if (n == 1 || n == 2) return 1;
	int current = 0, prev1 = 1, prev2 = 1;
	for (int i = 3; i < n + 1; i++)
	{
		current = prev1 + prev2;
		prev1 = prev2;
		prev2 = current;
	}

	return current;
}

相比于动态规划,这个方法极大地节省空间,减低空间复杂度。

d,矩阵快速幂

涉及到矩阵计算,有点复杂,需要前置知识。

1,原理

2,代码展示

a,矩阵乘法

补充:矩阵怎么作为参数传参

  • 使用指针:最常见的方式,适用于动态数组或不确定大小的数组。
  • 使用数组类型声明:当数组的大小已知时(如固定行列数),可以直接在函数签名中声明数组的尺寸。
  • 指向指针的指针:处理动态二维数组或不规则二维数组时非常有用。
  • typedef 定义类型:为二维数组定义新的类型,代码更加简洁和可读。
void matrixmultiply(int F[2][2], int M[2][2])
{
	int x = F[0][0] * M[0][0] + F[0][1] * M[1][0];
	int y = F[0][0] * M[0][1] + F[0][1] * M[1][1];
	int z = F[1][0] * M[0][0] + F[1][1] * M[1][0];
	int w = F[1][0] * M[0][1] + F[1][1] * M[1][1];

	F[0][0] = x;
	F[0][1] = y;
	F[1][0] = z;
	F[1][1] = w;
}

这是对于两行两列矩阵。

b,矩阵的幂

void matrixpowder(int F[2][2], int n)
{
	if (n == 0 || n == 1) return;//when n is 0 or 1,the matrix dont need processing
	int M[2][2] = { {1,1},{1,0} };
	matrixpowder(F, n / 2);
	matrixmultiply(F, F);
	if (n % 2 != 0) matrixmultiply(F, M);
}

 这里用到了递归算法

接下来是解释这串代码:

首先传入的参数F,必须是用于计算斐波那契数列的标准矩阵。数学原理如下:

即求F(n)就是算M的n-1次幂后的矩阵(假设为A)的第一行第一列元素。

然后是if语句判断,当n=1或0时,0是单位矩阵,1是其本身,不做处理。

matrixpowder(F, n / 2);

这行是利用二分法求高幂次的矩阵,对于偶数次幂,二分求F的n/2次幂。一直二分直至n=1或0.后面有矩阵乘法。

	matrixmultiply(F, F);

注意这里的F不再是传入的F,而是二分后的F,所以算的是计算矩阵 Fn=Fn/2×Fn/2Fn=Fn/2×Fn/2

最后的代码是用于处理幂是奇数,即

  • 对于奇数 n,Fn=Fn−1×Fn=Fn−1×F,即矩阵的 n 次方可以通过先计算 Fn−1 再乘以 F 来得到。
  • 这里,matrixMultiply(F, M) 用于将矩阵 F 再乘上矩阵 M,从而补偿掉少计算的一次矩阵乘法。
    • 如果 n=5,首先计算 F2 并将其平方得到 F4,然后再与 M 相乘得到 F5。

完整代码展示

#include<iostream>

using namespace std;

void matrixmultiply(int F[2][2], int M[2][2]);
void matrixpowder(int F[2][2], int);
int Fibonacci(int);


int main()
{
	cout << Fibonacci(10) << endl;
	return 0;
}

void matrixmultiply(int F[2][2], int M[2][2])
{
	int x = F[0][0] * M[0][0] + F[0][1] * M[1][0];
	int y = F[0][0] * M[0][1] + F[0][1] * M[1][1];
	int z = F[1][0] * M[0][0] + F[1][1] * M[1][0];
	int w = F[1][0] * M[0][1] + F[1][1] * M[1][1];

	F[0][0] = x;
	F[0][1] = y;
	F[1][0] = z;
	F[1][1] = w;
}


void matrixpowder(int F[2][2], int n)
{
	if (n == 0 || n == 1) return;//when n is 0 or 1,the matrix dont need processing
	int M[2][2] = { {1,1},{1,0} };
	matrixpowder(F, n / 2);
	matrixmultiply(F, F);
	if (n % 2 != 0) matrixmultiply(F, M);
}

int Fibonacci(int n)
{
	if (n == 1 || n == 2) return 1;
	int F[2][2] = { {1,1},{1,0} };
	// the standard matrix for calculating the power of matrix

	matrixpowder(F, n - 1);
	return F[0][0];
}
优点:
  • 时间复杂度:O(log⁡n)O(logn)(通过快速幂)
  • 空间复杂度:O(1)O(1)

e,黄金分割法

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

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

相关文章

解决程序因缺少xinput1_3.dll无法运行的有效方法,有效修复丢失xinput1_3.dll

如果你的电脑在运行某些应用程序或游戏时提示“xinput1_3.dll丢失”或“找不到xinput1_3.dll”的错误消息&#xff0c;那么很可能是因为你的系统中缺少这个重要的DLL文件而导致的问题。那么电脑出现xinput1_3.dll丢失的问题时有哪些方法进行修复呢&#xff1f; 如何确定电脑是否…

深入了解区块链:Web3的基础架构与发展

在数字时代的浪潮中&#xff0c;区块链技术正逐渐成为Web3的重要基础&#xff0c;重新定义互联网的结构和用户体验。Web3不仅是一个全新的网络阶段&#xff0c;更代表了一种去中心化的理念&#xff0c;强调用户主权和数据隐私。本文将深入探讨区块链在Web3中的基础架构、技术特…

内核tracepoint的注册回调及添加的方法

一、背景 内核开发时往往需要做一些内核态函数的监测或者内核状态的监测&#xff0c;就需要用一些调试手段来观测。常用的内核态的观测如kprobe和tracepoint&#xff0c;但是kprobe往往受制于一些系统的限制&#xff0c;很多系统并没有打开kprobe选项&#xff0c;这样我们不能…

React融合css

单纯使用tsx文件生成的页面比较单一&#xff0c;可以考虑结合css进行使用&#xff0c;需要说明的是&#xff0c;本人水平有限&#xff0c;仅对接触过的几种方式进行说明 内联样式 内联样式也有多种写法&#xff0c;此处仅列举两种比较简单的写法 写法一 import React from …

【大数据学习 | kafka高级部分】kafka中的选举机制

controller的选举 首先第一个选举就是借助于zookeeper的controller的选举 第一个就是controller的选举&#xff0c;这个选举是借助于zookeeper的独享锁实现的&#xff0c;先启动的broker会在zookeeper的/contoller节点上面增加一个broker信息&#xff0c;谁创建成功了谁就是主…

【基于PSINS工具箱】以速度为观测量的SINS/GNSS组合导航,UKF滤波

基于【PSINS工具箱】&#xff0c;提供一个MATLAB例程&#xff0c;仅以速度为观测量的SINS/GNSS组合导航&#xff08;滤波方式为UKF&#xff09; 文章目录 工具箱程序简述运行结果 代码程序讲解MATLAB 代码教程&#xff1a;使用UKF进行速度观测1. 引言与基本设置2. 初始设置3. U…

ThinkBook 14+ 2024 Ubuntu 触控板失效 驱动缺失问题解决

首先我的电脑是thinkbook14 2024&#xff0c;从ubuntu18到ubuntu24&#xff0c;笔者整个都试了一遍&#xff0c;触摸板都没反应&#xff0c;确认不是linux系统内核问题&#xff0c;原因为驱动缺失。 解决步骤&#xff1a; &#xff08;1&#xff09;下载驱动&#xff0c;网址如…

如何使用 Web Scraper API 高效采集 Facebook 用户帖子信息

目录 前言一、什么是Web Scraper API二、Web Scraper API 的优势&#xff1a;三、Web Scraper API 适用场景四、实践案例目标需求视频讲解1、选择Web Scraper API2、登录注册3、进入用户控制面板4、选择API5、触发数据收集 API6、获取爬虫结果7、分析爬虫结果&#xff08;1&…

Qt_day3_信号槽

目录 信号槽 1. 概念 2. 函数原型 3. 连接方式 3.1 自带信号 → 自带槽 3.2 自带信号 → 自定义槽 3.3 自定义信号 4. 信号槽传参 5. 对应关系 5.1 一对多 5.2 多对一 信号槽 1. 概念 之前的程序界面只能看&#xff0c;不能交互&#xff0c;信号槽可以让界面进行人机…

Elastic 通用分析:提高性能并降低成本

作者&#xff1a;来自 Elastic Luca Wintergerst•Tim Rhsen 在这篇博客中&#xff0c;我们将介绍我们的一位工程师的一项发现如何帮助我们在 QA 环境中节省数千美元的成本&#xff0c;并且一旦我们将这一变化部署到生产中&#xff0c;还可以节省更多的成本。 在当今的云服务和…

【WRF理论第十一期】检查WPS输出:geogrid和metgrid 的输出nc数据+ungrib输出WPS格式

【WRF理论第十一期】检查WPS输出&#xff1a;geogrid和metgrid输出nc数据ungrib输出WPS格式 检查WPS输出WPS 输出检查的重要性使用 NetCDF 格式查看 geogrid 和 metgrid 的输出检查和可视化数据的工具 ungrib 输出数据的格式使用 plotfmt 工具查看 ungrib 输出 参考 上一篇博客…

万字长文解读深度学习——卷积神经网络CNN

推荐阅读&#xff1a; 卷积神经网络&#xff08;CNN&#xff09;详细介绍及其原理详解 CNN笔记&#xff1a;通俗理解卷积神经网络 文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;主要组件输入层卷积层 (Convolutional Layer)批归一化层&#xff08;Batch Normalizat…

Redis生产问题(缓存穿透、击穿、雪崩)——针对实习面试

目录 Redis生产问题什么是缓存穿透&#xff1f;如何解决缓存穿透&#xff1f;什么是缓存击穿&#xff1f;如何解决缓存击穿&#xff1f;缓存穿透和缓存击穿有什么区别&#xff1f;什么是缓存雪崩&#xff1f;如何解决缓存雪崩&#xff1f; Redis生产问题 什么是缓存穿透&#x…

19、centos7优化

优化条目&#xff1a; 优化条目&#xff1a; 1.sudo管理用户授权 &#xff08;不用root管理,以普通用户的名义通过sudo提权&#xff09; 2.更改默认的远程连接SSH服务端口,禁止root用户远程连接,&#xff08;提前建立普通用户&#xff09;&#xff08;甚至更改为只监听内网IP…

河北省内首台心磁图仪正式落户河北梅奥心血管病医院

河北省内首台心磁图仪正式落户河北梅奥心血管病医院。 2024年11月9日&#xff0c;河北梅奥心血管病医院迎来了一场激动人心的历史时刻——河北省首台心磁图仪启用仪式在医院内隆重举行&#xff0c;标志着这一顶尖医疗设备正式入驻&#xff0c;为医院心脏影像诊断技术开启了全新…

【C语言刷力扣】283.移动零

题目&#xff1a; 解题思路&#xff1a; 将不为 0 的元素依次放在数组前面&#xff0c;再在数组末尾补上 0。 时间复杂度&#xff1a; 空间复杂度&#xff1a; void moveZeroes(int* nums, int numsSize) {int i 0, j 0;for (; i < numsSize; i) {if (nums[i]) {nums…

网络初阶——应用层:HTTPS 协议

一、HTTPS & HTTP 的区别 从协议的名字来看&#xff0c;HTTP 比 HTTPS 少了一个 S。而这个 “S”&#xff0c;其实可以理解成 “Safe”&#xff0c;所以不难看出&#xff0c;其实 HTTPS 就是 HTTP 的安全版。就是为了保证客户端 cookie 的传输安全的。 二、相关概念 1、明…

怎么禁止Ubuntu自动更新升级

怎么禁止Ubuntu自动更新升级 笔者在做MIT 6.S081的时候发现他给我的qemu自动更新了又卡住了&#xff0c;故关闭了自动更新 文章目录 怎么禁止Ubuntu自动更新升级一、图形化修改二、基于命令行修改配置文件的方法 一、图形化修改 1.打开设置->软件和更新->更新 2.选择自…

Spring Boot框架:构建符合工程认证的计算机课程

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

机器学习—选择激活函数

可以为神经网络中的不同神经元选择激活函数&#xff0c;我们将从如何为输出层选择它的一些指导开始&#xff0c;事实证明&#xff0c;取决于目标标签或地面真相标签y是什么&#xff0c;对于输出层的激活函数&#xff0c;将有一个相当自然的选择&#xff0c;然后看看激活函数的选…