剑指offer练习日志02:基于矩阵乘法求斐波那契数列通项

news2024/10/7 8:21:43

目录

一.矩阵乘法与斐波那契数列

1.利用数列的项构造二阶方阵

2.引入矩阵乘法

二.算法实现

1.MatrixFib对象成员变量

2.MatrixFib对象的构造函数

3. MatrixFib对象的成员算法接口

4.对象测试


 

一.矩阵乘法与斐波那契数列

1.利用数列的项构造二阶方阵

  • 😄现定义斐波那契数列:

  • 😄再构造二阶方阵An:其中n>=2(我们可以称之为斐波那契方阵)
  • 😄当n=2时,A2为下图所示的方阵:

2.引入矩阵乘法

  • 😍接下来计算如下矩阵相乘的结果: 
  •  😍进一步得:
  • 😍即:
  • 😍于是我们可以得到二阶斐波那契方阵递推公式:
  • 😍通过上面的递推公式我们可以得到斐波那契方阵通项公式:
  • 😍于是通过计算一个特殊的二阶方阵的n-1次方,再取出其中第一行第一列元素我们就可以得到斐波那契数列的第n项
  • 😍接着我们记二阶方阵a:

😍再构造关于a的n-1次方递推公式

😍基于上述的数学结论,我们可以设计出时间复杂度为O(logN)的算法用于求斐波那契数列的第n项.

二.算法实现

😜我们通过封装一个MatrixFib对象的方式来实现时间复杂度为O(logN)的求斐波那契数列通项算法

😜先给出总体代码:

class MatrixFib
{
public:
	//构造函数初始化成员变量
	MatrixFib()
	{
		_BacMatrix.push_back({ 1,1 });
		_BacMatrix.push_back({ 1,0 });

		_RetMatrix.push_back({ 1,1});
		_RetMatrix.push_back({ 1,0});

		_TemMatrix.push_back({ 1,1 });
		_TemMatrix.push_back({ 1,0 });
	}


	int Fib(int n)
	{
		assert(n >= 0);
		if (0 == n)
		{
			return _BacMatrix[1][1];
		}
		else if (1 == n)
		{
			return _BacMatrix[0][1];
		}
		else
		{
			return PowerOfMatrix(n-1)[0][0];
		}
	}

private:
	vector<vector<int>>& PowerOfMatrix(int n)
	{
		if (1 >= n)
		{
			return _BacMatrix;
		}
		else if(1 == n%2)
		{
			//n为奇数时的递归路径
			PowerOfMatrix(n / 2);
			_RetMatrix = MatrixMul(_RetMatrix,_RetMatrix);
			_RetMatrix = MatrixMul(_RetMatrix, _BacMatrix);
			return _RetMatrix;
		}
		else
		{
			//n为偶数时的递归路径
			PowerOfMatrix(n / 2);
			_RetMatrix = MatrixMul(_RetMatrix, _RetMatrix);
			return _RetMatrix;
		}
	}

	vector<vector<int>>& MatrixMul(vector<vector<int>>& M1, vector<vector<int>>& M2)
	{
		int i = 0;
		int j = 0;
		for (i = 0; i < _row; ++i)
		{
			for (j = 0; j < _col; ++j)
			{
				int sum = 0;
				for (int k = 0; k < _col; ++k)
				{
					sum += M1[i][k] * M2[k][j];
				}
				_TemMatrix[i][j] = sum;
			}
		}
		return _TemMatrix;
	}


private:
	//二阶方阵的行列数
	static const int _row = 2;
	static const int _col = 2;

	//用于存储特殊二阶方阵的容器
	vector<vector<int>> _BacMatrix;
	//用于存放特殊二阶方阵的幂的容器
	vector<vector<int>> _RetMatrix;
	//用于临时储存计算结果的容器
	vector<vector<int>> _TemMatrix;
};
  • 😜接下来分模块进行拆解分析

1.MatrixFib对象成员变量

😜MatrixFib对象的成员变量:

class MatrixFib
{
public:


private:
	//二阶方阵的行列数
	static const int _row = 2;
	static const int _col = 2;

	//用于存储特殊二阶方阵的容器
	vector<vector<int>> _BacMatrix;
	//用于存放特殊二阶方阵的幂的容器
	vector<vector<int>> _RetMatrix;
	//用于临时储存计算结果的容器
	vector<vector<int>> _TemMatrix;
};
  1. 😜之所以要定义_TemMatrix成员容器是为了能够避免在后续算法接口中出现大量的vector对象之间的深拷贝从而导致算法性能降低(对象传参尽量传引用)

  2. 😜所谓特殊二阶方阵:

2.MatrixFib对象的构造函数

	//构造函数初始化成员变量
	MatrixFib()
	{
		_BacMatrix.push_back({ 1,1 });
		_BacMatrix.push_back({ 1,0 });

		_RetMatrix.push_back({ 1,1});
		_RetMatrix.push_back({ 1,0});

		_TemMatrix.push_back({ 1,1 });
		_TemMatrix.push_back({ 1,0 });
	}
  • 用该构造函数实现MatrixFib对象成员变量初始化

3. MatrixFib对象的成员算法接口

  • 😜求二阶方阵M1和M2乘积私有成员接口:
    	vector<vector<int>>& MatrixMul(vector<vector<int>>& M1, vector<vector<int>>& M2)
    	{
    		int i = 0;
    		int j = 0;
    		for (i = 0; i < _row; ++i)
    		{
    			for (j = 0; j < _col; ++j)
    			{
    				int sum = 0;
    				for (int k = 0; k < _col; ++k)
    				{
    					sum += M1[i][k] * M2[k][j];
    				}
    				_TemMatrix[i][j] = sum;
    			}
    		}
    		return _TemMatrix;
    	}
  • 😜_TemMatrix是成员容器,用来暂时存放两个方阵相乘的结果

  • 😜根据之前的数学推导,我们接下来需要设计一个求特殊二阶方阵n次幂的成员接口:

    😜根据上面的递推公式我们可以在O(logN)的时间复杂度下实现这个私有成员接口

    	vector<vector<int>>& PowerOfMatrix(int n)
    	{
    		if (1 >= n)
    		{
    			return _BacMatrix;
    		}
    		else if(1 == n%2)
    		{
    			//n为奇数时的递归路径
    			PowerOfMatrix(n / 2);
    			_RetMatrix = MatrixMul(_RetMatrix,_RetMatrix);
    			_RetMatrix = MatrixMul(_RetMatrix, _BacMatrix);
    			return _RetMatrix;
    		}
    		else
    		{
    			//n为偶数时的递归路径
    			PowerOfMatrix(n / 2);
    			_RetMatrix = MatrixMul(_RetMatrix, _RetMatrix);
    			return _RetMatrix;
    		}
    	}
  • 😜注意_RetMatrix,_BacMatrix都是类的成员容器

  • 😜最后再提供一个public的Fib成员接口,使得外界可以调用它来求斐波那契数列的第n项:

    	int Fib(int n)
    	{
    		assert(n >= 0);
    		if (0 == n)
    		{
    			return _BacMatrix[1][1];
    		}
    		else if (1 == n)
    		{
    			return _BacMatrix[0][1];
    		}
    		else
    		{
    			return PowerOfMatrix(n-1)[0][0];
    		}
    	}
  • 😜注意PowerOfMatrix接口返回的是容器的引用

4.对象测试

  • 剑指 Offer 10- II. 青蛙跳台阶问题 - 力扣(Leetcode)https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/solutions/2241390/yong-fei-bo-na-qi-er-jie-fang-zhen-shi-x-z7hg/

  • 😜将MatrixFib对象稍作修改就可以用于题解测试

  • 😜题解代码:

    class Solution 
    {
    public:
    	//构造函数初始化成员变量
    	Solution()
    	{
    		_initMatrix.push_back({ 2,1 });
    		_initMatrix.push_back({ 1,1 });
    
    		_BacMatrix.push_back({ 1,1 });
    		_BacMatrix.push_back({ 1,0 });
    
    		_RetMatrix.push_back({ 1,1});
    		_RetMatrix.push_back({ 1,0});
    
    		_TemMatrix.push_back({ 1,1 });
    		_TemMatrix.push_back({ 1,0 });
    	}
    
        //求斐波那契数列第n项的公共接口
    	int numWays(int n)
    	{
    		assert(n >= 0);
    		if (0 == n)
    		{
    			return _initMatrix[1][1];
    		}
    		else if (1 == n)
    		{
    			return _initMatrix[1][1];
    		}
    		else if(2 == n)
    		{
    			return _initMatrix[0][0];
    		}
    		else
    		{
    			return MatrixMul(PowerOfMatrix(n - 2), _initMatrix)[0][0];
    		}
    	}
    
        //求特殊二阶方阵的n次幂的递归接口
    	vector<vector<unsigned long long>>& PowerOfMatrix(int n)
    	{
    		if (1 >= n)
    		{
    			return _BacMatrix;
    		}
    		else if(1 == n%2)
    		{
    			PowerOfMatrix(n / 2);
    			_RetMatrix = MatrixMul(_RetMatrix,_RetMatrix);
    			_RetMatrix = MatrixMul(_RetMatrix, _BacMatrix);
    			return _RetMatrix;
    		}
    		else
    		{
    			PowerOfMatrix(n / 2);
    			_RetMatrix = MatrixMul(_RetMatrix, _RetMatrix);
    			return _RetMatrix;
    		}
    	}
    
    
        //计算两个矩阵相乘结果的接口
    	vector<vector<unsigned long long>>& MatrixMul
                                            (vector<vector<unsigned long long>>& M1		                            
                                            ,vector<vector<unsigned long long>>& M2)
    	{
    		int i = 0;
    		int j = 0;
    		for (i = 0; i < _row; ++i)
    		{
    			for (j = 0; j < _col; ++j)
    			{
    				unsigned long long sum = 0;
    				for (int k = 0; k < _col; ++k)
    				{
    					sum += M1[i][k] * M2[k][j];
    				}
    				_TemMatrix[i][j] = sum%1000000007;
    			}
    		}
    		return _TemMatrix;
    	}
    
    
    private:
    	//二阶方阵的行列数
    	static const int _row = 2;
    	static const int _col = 2;
        
        //用于存储特殊二阶方阵
    	vector<vector<unsigned long long>> _BacMatrix;
        //用于存储由F2,F1,F0构成的二阶方阵
    	vector<vector<unsigned long long>> _initMatrix;
        
        //用于存储Fn,Fn-1,Fn-1,Fn-2构成的二阶方阵
    	vector<vector<unsigned long long>> _RetMatrix;
        //用于存储矩阵相乘的临时计算结果
    	vector<vector<unsigned long long>> _TemMatrix;
    };
    
    

 

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

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

相关文章

【项目实战-CRM】(二:物理模型设计 搭建CRM项目环境)

文章目录 1.物理模型设计2.搭建开发环境2.1 创建crm项目2.2 创建模块2.3 添加jar包2.4 相关配置2.4.1 MyBatis 配置2.4.2 配置数据连接和事务2.4.3 SpringMVC配置文件2.4.4 Spring总配置文件2.4.5 web.xml2.4.6 设置maven对配置文件的编译选项 2.5 添加静态页面资源2.6 将项目部…

在VMmare上安装Windows 2003

今天和大家介绍一下如何使用VMmare安装一个Windows 2003 Enterprise Edition操作系统 首先小陈先下载了一个VMmare 10虚拟软件&#xff0c;将其安装在自己的电脑上。 一、新建虚拟机 然后打开VMmare软件&#xff0c;在窗口的菜单栏里点击“文件”-->“新建虚拟机” 这边让…

c++验证用户输入合法性的示例代码

c验证用户输入合法性的示例代码 本文介绍c验证用户输入合法性&#xff0c;用于检测限定用户输入值。包括&#xff1a;1、限定用户输入为整数&#xff08;正负整数&#xff09;&#xff1b;2、限定用户输入为正整数&#xff1b;3、限定用户输入为正数&#xff08;可以含有小数&…

AI算力碎片化:矩阵乘法的启示

尽管AI的发展取得了巨大进步&#xff0c;但编译器LLVM之父Chris Lattner认为&#xff0c;AI技术应用并不深入&#xff0c;远远没有发挥出已有机器学习研究的所有潜力。而AI系统和工具的单一化和碎片化正是造成这一问题的根源。 为了让AI发挥其真正的潜力&#xff0c;计算碎片化…

Oracle中实现恢复删除的表或表数据内容

一、需求说明 在我们进行项目开发或运维过程中,由于操作不当,引起的误删Oracle数据库表或指定表的数据内容,导致程序出现故障;而我们又没有对数据库进行备份,此时,如果不能及时恢复数据库内容将会导致严重的事故。我们需要一种能够补救的方法来挽回损失,恢复被误删的表或…

WEB攻防-弱口令暴力破解(包含工具、字典下载地址)

目录 一、弱口令概述 二、Web类-加密&验证码后台猜解 三、服务类-SSH&RDP远程终端猜解 四、应用类-ZIP&Word文件压缩包猜解 一、弱口令概述 弱口令(weak password) 没有严格和准确的定义&#xff0c;通常认为容易被别人&#xff08;他们有可能 对你很了解&#…

025:Mapbox GL加载栅格高程模型raster-dem文件

第025个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载image图像文件。栅格 DEM 源。 仅支持 Mapbox Terrain-DEM,您可以将 Terrain-DEM 用于各种视觉和分析应用程序,从样式化地形坡度和山体阴影到为视频游戏生成 3D 地形网格。 直接复制下面的 vue+mapbo…

记录一个dpdk 19.11 hello world 跑不起来问题(编译和权限)

下载dpdk 源码后: git clone gitgithub.com:DPDK/dpdk.git 切到 19.11 git checkout v19.11 用usertool下的dpdk_setup.sh 选择 [38] x86_64-native-linuxapp-gcc 进行编译: 结果者是build 不过: Build kernel/linux/igb_uio Build kernel/linux/kni CC [M] /home/t…

Linux查看GPU信息和使用情况

1.Linux查看显卡信息 lspci | grep -i vga 2.使用nvidia GPU lspci | grep -i nvidia 个人感觉看不出什么信息&#xff0c;除了显存大小&#xff0c;另外就是可以通过加入前面的显卡编号&#xff0c;显示更加详细的信息。 lspci -v -s 00:0f.0 3.Linux查看Nvidia显卡信息及使…

软件测试之学习测试用例的设计(等价类法、边界值法、错误猜测法、场景法、因果图法、正交法)

文章目录 1. 测试用例的概念2. 为什么在测试前要设计测试用例3. 基于需求进行测试用例的设计1&#xff09;功能性需求测试2&#xff09;非功能性需求测试 4. 具体的测试用例设计方法1&#xff09;等价类2&#xff09;边界值3&#xff09;错误猜测法4&#xff09;场景法5&#x…

Python入门教程+项目实战-11.1节: 元组的基础概念

目录 11.1.1 理解元组类型 11.1.2 元组的类型名 11.1.3 元组的定义 11.1.4 元组的解包 11.1.5 遍历可迭代对象 11.1.6 本节知识要点 11.1.7 系统学习python 11.1.1 理解元组类型 元组与列表有着相同的数据结构&#xff0c;区别在于&#xff0c;元组是静态的数据类型&am…

本地如何搭建一个Stable Diffusion 的AI绘画工具?

实现AI绘画自由指南 前期准备安装1.安装 Homebrew 工具2. 安装Python33.下载 Stable Diffusion -webui4.下载大模型5. 安装 GFPGAN&#xff08;神坑&#xff09;5. 允许 stable diffusion-webui 如何使用效果图 最近看到网上各种AI工具很是火爆&#xff0c;心里也是有点痒痒&am…

消防应急照明和疏散指示系统在轨道交通中的设计应用

摘要&#xff1a;本文分析了消防应急照明和疏散指示系统的特点与设计要点&#xff0c;介绍了系统在城市轨道交通中的设计应用&#xff0c;轨道交通设计中新的消防应急照明和疏散指示系统的备用照明仍由EPS供电&#xff0c;新增一套疏散指示照明系统&#xff0c;增加疏散照明指示…

Beta成果测试总结

Beta成果测试总结 Beta是一个项目的早期测试&#xff0c;通过 Beta能够初步的了解整个系统的稳定性&#xff0c;测试系统是否能够满足客户的需求。我们可以在测试过程中发现一些问题&#xff0c;从而快速解决。 当我们在测试一个新系统时&#xff0c;我们需要进行测试前的准备工…

Node.js的简介

一、什么是node.js Node.js是JavaScript语言的服务器运行环境。 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。 Node.js是一个事件驱动I/O服务端JavaScript环境&#xff0c;基于Google的V8引擎&#xff0c;V8引擎执行…

一文带你全面了解最火爆的ChatGpt

导读 OpenAI近期发布聊天机器人模型ChatGPT&#xff0c;迅速出圈全网。它以对话方式进行交互。以更贴近人的对话方式与使用者互动&#xff0c;可以回答问题、承认错误、挑战不正确的前提、拒绝不适当的请求。高质量的回答、上瘾式的交互体验&#xff0c;圈内外都纷纷惊呼。 为什…

【ONE·C++ || 继承】

总言 主要介绍继承相关内容。 文章目录 总言1、继承介绍1.1、继承是什么1.2、继承方式与访问限定符1.3、继承作用域 2、基类和派生类对象赋值转换2.1、子类对象可以赋值给父类对象/指针/引用2.2、基类对象不能赋值给派生类对象2.3、基类的指针可以通过强制类型转换赋值给派生类…

flask学习-实践02

项目实战 入门文当(2条消息) python flask框架详解_flask python_尘世风的博客-CSDN博客(2条消息) python flask框架详解_flask python_尘世风的博客-CSDN博客 入门项目 抄作业了&#xff01;6 大 Flask 开源实战项目推荐_小詹学 Python的博客-CSDN博客 (66 条消息) GitHub 上有…

DataStructure--Tree

文章摘录链接 1.树基本概念 计算机数据结构中的树就是对显示中的树的一种抽象&#xff08;倒置现实中的树&#xff09;。 1.1 树 有层次关系N&#xff08;N≥0&#xff09;个节点的有限集合空树&#xff1a; N0 非空树&#xff1a; 有且只有一个根节点1.2 节点 根节点 分…

MongoDB【MongoRepository MongoTemplate】实现增删改查

目录 1&#xff1a;文章评论 1.1&#xff1a;需求分析 1.2&#xff1a;表结构分析 1.3&#xff1a;技术选型 1.3.1&#xff1a;mongodb-driver 1.3.2&#xff1a;SpringDataMongoDB 1.4&#xff1a;文章微服务模块搭建 1.5&#xff1a;文章评论实体类的编写 1.6&#x…