算法学习笔记五 斐波那契数列
斐波那契数列
一、最基本的
所以,只要知道这个数列的前两项,就可以求出之后所有项了。
核心部分(最简单的递推方法,但是范围是n<=48,否则会超时and溢出):
#include <cstdio> //头文件
int main()
{double f[50];int n, i;f[0] = 0; //其实并没有用f[1] = 1;f[2] = 1; //两个初始值scanf_s("%d", &n);for (i = 3; i <= n; i++)f[i] = f[i - 1] + f[i - 2]; //开始使用斐波那契数列printf("%0.2lf", f[n]); //输出,保留两位小数return 0;
}
【注意,这种简单方法还有一种表示,就是用函数写,把递推式变成函数的递归调用,也很好理解。但是亲测会比递推式的时间长很多,n>35的时候就TLA了。因此这提示我们,函数的递归是很耗时的,不要多用。同时上述方法比较快的一个原因是用数组储存时,许多已经算过的数就不用算了,当然会快很多】
二、快速做法(矩阵乘法+快速幂 )
矩阵乘法推导
F i = F i − 1 + F i − 2 F_i = F_{i-1} + F_{i-2} Fi=Fi−1+Fi−2
F i − 1 = F i − 1 + 0 F_{i-1} = F_{i-1} + 0 Fi−1=Fi−1+0
把两个式子转化为矩阵形式,即(都是2x2的方便写代码)
{ F i 0 F i − 1 0 } = { 1 1 1 0 } ∗ { F i − 1 0 F i − 2 0 } \left\{\begin{matrix}F_i&0\\F_{i-1}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}*\left\{\begin{matrix}F_{i-1}&0\\F_{i-2}&0\end{matrix}\right\} {FiFi−100}={1110}∗{Fi−1Fi−200}
{ F i − 1 0 F i − 2 0 } = { 1 1 1 0 } ∗ { F i − 2 0 F i − 3 0 } \left\{\begin{matrix}F_{i-1}&0\\F_{i-2}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}*\left\{\begin{matrix}F_{i-2}&0\\F_{i-3}&0\end{matrix}\right\} {Fi−1Fi−200}={1110}∗{Fi−2Fi−300}
…一直到
{ F 3 0 F 2 0 } = { 1 1 1 0 } ∗ { F 2 0 F 1 0 } \left\{\begin{matrix}F_3&0\\F_{2}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}*\left\{\begin{matrix}F_{2}&0\\F_{1}&0\end{matrix}\right\} {F3F200}={1110}∗{F2F100}
总结即
{ F i 0 F i − 1 0 } = { 1 1 1 0 } ( i − 2 ) ∗ { F 2 0 F 1 0 } \left\{\begin{matrix}F_i&0\\F_{i-1}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}^{(i-2)}*\left\{\begin{matrix}F_{2}&0\\F_{1}&0\end{matrix}\right\} {FiFi−100}={1110}(i−2)∗{F2F100}
即
{ F i 0 F i − 1 0 } = { 1 1 1 0 } ( i − 2 ) ∗ { 1 0 1 0 } \left\{\begin{matrix}F_i&0\\F_{i-1}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}^{(i-2)}*\left\{\begin{matrix}1&0\\1&0\end{matrix}\right\} {FiFi−100}={1110}(i−2)∗{1100}
基于此方法,可以看到我们要求的就是一个矩阵的i-2次幂即可,然后 F i F_i Fi就等于结果再乘以一个[1 0 ,1 0] 的第 [0][0] 个元素。
而求幂,我们可以采用快速幂的方法:
快速幂
快速幂算法的核心思想就是每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。
戳下面链接理解快速幂原理:
快速幂算法 刘杨俊
//base为底数,power为指数,求结果的后三位(求后三位就对结果对1000取模即可)
#include<iostream>
using namespace std;long long fastPower(long long base, long long power)
{long long result = 1;while (power > 0) {if (power % 2 == 1)//如果指数为奇数result = result * base % 1000;//相当于结果里已经乘过一次被分离出来的底数了power = power / 2; //不管对于奇数/偶数,除以2就是整数base = base * base % 1000; //指数除2,则底数平方cout << "base:" << base << "power:" << power << endl;}
return result;
}int main() {int base, power;cin >> base >> power;cout << fastPower(base, power);return 0;
}
运行结果:
矩阵快速幂
首先矩阵乘法的模板长这样,还是很好理解的:
int main()
{int a[110][110] = {};int b[110][110] = {};int c[110][110] = {};int n = 0, m = 0, p = 0;cin >> n >> m;//矩阵a为n*m(n行m列) for (int i = 0; i < n; i++) //一行一行地输入for (int j = 0; j < m; j++)scanf("%d", &a[i][j]);cin >> p; //矩阵b为m*p(m行p列)for (int i = 0; i < m; i++)for (int j = 0; j < p; j++)scanf("%d", &b[i][j]);//重点在这里for (int i = 0; i < n; i++) //矩阵c是a与b相乘得到的 for (int j = 0; j < p; j++) //n*p(n行p列) for (int k = 0; k < m; k++)c[i][j] += a[i][k] * b[k][j]; //注意是求多项的和,是+=for (int i = 0; i < n; i++){for (int j = 0; j < p; j++)cout << c[i][j] << " ";cout << endl;}return 0;
}
矩阵快速幂即对矩阵使用快速幂的思想,既然求幂了就说明这是个方阵,设为NxN,对于方阵的乘法就要简洁很多:
#include <bits/stdc++.h>using namespace std;struct Matrix {long long a[N][N];
}; //定义一个结构,方便作为返回值,并且进行重载运算符,不然要写很多次函数hhhMatrix operator * (Matrix a, Matrix b) { //重载对Matrix类型变量的运算符*(乘号),就是写的一个乘法,a和b都是NxN的方阵Matrix ans;memset(ans.a, 0, sizeof(ans.a)); //初始化为0for (int i = 0; i < N; i++)for (int j = 0; j < N; j++)for (int k = 0; k < N; k++)ans.a[i][j] += a.a[i][k] * b.a[k][j];return ans;
}
矩阵快速幂:把快速幂的思想应用于矩阵的方阵乘法【这里就是快速幂的核心了,和前面实数是一样的,如果指数是奇数则把底数y取出来,如果是偶数就使底数平方,指数-1…】
Matrix power(Matrix a, int p) { //对a求p次幂的函数Matrix y = a, k = a; //y是一个中间变量,k是结果int t = p;while (t) {if (t & 1) k = k * y; //t&1,若t为奇数,则结果为1!y = y * y;t >>= 1; //t-1的高端写法!}return k;
}
完整代码
把矩阵快速幂应用于斐波那契数列:
{ F i 0 F i − 1 0 } = { 1 1 1 0 } ( i − 2 ) ∗ { 1 0 1 0 } \left\{\begin{matrix}F_i&0\\F_{i-1}&0\end{matrix}\right\}= \left\{\begin{matrix}1&1\\1&0\end{matrix}\right\}^{(i-2)}*\left\{\begin{matrix}1&0\\1&0\end{matrix}\right\} {FiFi−100}={1110}(i−2)∗{1100}
就是,当求Fn的时候,只要求[1 1 ,1 0]的n-2次幂乘以[1 0 , 1 0]:
//所有数字的定义一定要记得是long long......血的教训#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;const int N = 3;
const long long mod = 1000000007; //一般结果都很大!要取模struct Matrix {long long out[N][N];
}; //定义一个结构,方便作为返回值,并且进行重载运算符,不然要写很多次函数hhhMatrix operator * (Matrix a, Matrix b) {//重载对Matrix类型变量的运算符*(乘号),就是写的一个乘法,a和b都是NxN的方阵Matrix ans;memset(ans.out, 0, sizeof(ans.out)); //初始化为0for (int i = 1; i < N; i++)for (int j = 1; j < N; j++)for (int k = 1; k < N; k++)ans.out[i][j] = ans.out[i][j] + (a.out[i][k] * b.out[k][j]) % mod;return ans;
}Matrix power(long long p) { //对底数求p次幂的函数Matrix base;base.out[1][1] = 1;base.out[1][2] = 1;base.out[2][1] = 1;base.out[2][2] = 0;Matrix ans;ans.out[1][1] = 1;ans.out[1][2] = 0;ans.out[2][1] = 0;ans.out[2][2] = 1;long long t = p;while (t > 0) {if (t & 1) //t&1,若t为奇数,则结果为1!{Matrix tmp = ans * base;for (int i = 1; i < N; i++)for (int j = 1; j < N; j++)ans.out[i][j] = tmp.out[i][j];}Matrix tmp = base * base;for (int i = 1; i < N; i++)for (int j = 1; j < N; j++)base.out[i][j] = tmp.out[i][j];t >>= 1; //t-1的高端写法!}return ans;
}int main() {long long n;cin >> n;n = n - 2;if (n == 1 || n == 2){cout << 1;return 0;}Matrix result = power(n);long long feib = (result.out[1][1]+result.out[1][2]) % mod;printf("%lld\n", feib);return 0;
}
三、理论表达式
有这么一道题目很有意思的题:
如果不熟悉斐波那契数列可能还需要花费一些时间…所以在这里把这一点列出来,只要看出了这道题的本质就是求斐波那契数列,就非常的简单了。
(摘自洛谷 密期望的题解)
算法学习笔记五 斐波那契数列相关推荐
- java学习笔记之斐波那契数列
斐波那契数列计算公式为: f(n) = f(n-1)+ f(n-2) 基于此写了一个方法,用于输出一个长度为指定的斐波那契数列(从正数1开始, 即1,1 ,2 ,3 ,5 ....): static ...
- 经典算法(6)斐波拉契数列、兔子繁殖、跳台阶算法
写在前面: 我是「扬帆向海」,这个昵称来源于我的名字以及女朋友的名字.我热爱技术.热爱开源.热爱编程.技术是开源的.知识是共享的. 这博客是对自己学习的一点点总结及记录,如果您对 Java.算法 感兴 ...
- 初学算法——第二天:斐波那契数列
14天阅读挑战赛 1 定义 斐波那契数列的定义者,是意大利数学家莱昂纳多·斐波那契(Leonardo Fibonacci),生于公元1170年,卒于1250年,籍贯是比萨.他被人称作"比萨的 ...
- 算法优化:探索斐波那契数列的新航线
只有用水将心上的雾气淘洗干净,荣光才会照亮最初的梦想. --加西亚·马尔克斯 最近沉迷于算法,总是想着实现与优化,今天想到了斐波那契数列,原来没咋多想,现在回头再看看能不能输出点新花样?! 什么是斐波 ...
- 算法(1)斐波那契数列
1.0 问题描述 实现斐波那契数列,求第N项的值 2.0 问题分析 斐波那契数列最简单的方法是使用递归,递归和查表法同时使用,可以降低复杂度. 根据数列特点,同时进行计算的数值其实只有3个,所以可以使 ...
- 427-动态规划算法-斐波那契数列
动态规划算法求解斐波那契数列 状态:dp数组,存储已经求解的子问题的最优解 递归版本的动态规划算法 //参数n表示斐波那契数列中数字的个数. //返回相应个数的斐波那契数列数字的值. int fabn ...
- 数学之美|斐波那契数列与黄金分割
14天阅读挑战赛 系列文章目录 趣味算法(第二版)读书笔记: day1: 序章|学习的方法和目标. day2:算法之美|打开算法之门与算法复杂性 day3.算法之美|指数型函数对算法的影响实际应用 d ...
- 斐波那契数列递归与非递归精讲
斐波那契数列是学习算法的入门级算法,要对算法进行研究的话我们就必须的掌握斐波那契数列算法.以下从斐波那契数列的简介,递归算法和非递归算法给大家进行介绍. 简介: 斐波那契数列(Fibonacci ...
- JS实现给定参数数范围内的有条件求和(以质数求和与斐波那契数列求和为例)
文章目录 前言 一.应用场景 二.算法举例 1. 求斐波那契数列中的奇数之和 2. 质数求和 总结 前言 本文给出一种JavaScript算法,用以实现给定参数数范围内的有条件求和.并以求斐波那契数列 ...
最新文章
- android多音字排序,Android拼音排序
- 关于HTML5中Canvas的宽、高设置问题
- 发现 ASP.Net 的一个关于回车提交的 Bug ? 必须多于一个 Text 域回车提交,Server: ButtonX_Click 才能截获!...
- Windows上的Java线程CPU分析
- sql 联合查询_一张图看懂sql运行顺序
- Portal-Basic Java Web 应用开发框架:应用篇(八) —— 整合 Freemarker
- SQL Server 的存储过程[转]
- git太慢时的加速办法,测试有效
- 西门子博途v14 SP1 S7-1200之间的以太网双边通讯(两个S7-1200 在一个项目中)
- 变压器次级输出为0v的原因_加速tensorflow中的Google临时融合变压器2 0
- 套接字Socket的常见面试题及答案
- Vue-element-admin 基础模板
- 详解浏览器中的粘贴事件 paste onpaste 事件
- SAP中税码、税率、税务科目的几个表及其中的勾稽关系
- 转-零死角玩转stm32-高级篇之SDIO(4bit + DMA、支持SDHC、带协议分析)
- 怎样促进计算机专业发展,【计算机教学论文】怎样促进计算机技术应用及改善(共4879字)...
- android gpu 视频编码,Android Mp4视频录制(OpenGL实现篇,附DEMO)
- R语言独立性检验-基础
- u-boot启动流程简图 --木草山人
- 【LLYD】That 70s show: why the disco decade is back in fashion