斐波那契多种实现和优化,递归到动态规划的优化~~~~~~~~~~~~
对于递归的优化过程#
- 暴力递归
- 对于暴力递归,某些计算过程是重复的,因此可以优化成记忆化递归。
- 而对于递归问题,一般都可以转换为动态规划。
- 而对于动态规划,我们又可以对dp数组进行优化,比如滚动数组的思想,可以将二维dp数组优化为一维dp数组,可以将一维dp数组优化为几个变量。
暴力递归
public int fibonaci(int n) {
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
return fibonaci(n - 1) + fibonaci(n - 2);
}
对于暴力递归,我们的fibonaci(n - 1)和fibonaci(n - 2)有很多重复计算,我们要想办法把已经计算过的结果都存储起来!
记忆化递归
public int fibonaci2(int n) {
if (n <= 0) {
return 0;
}
int[] memories = new int[n + 1];
return process2(n, memories);
}
private int process2(int n, int[] memories) {
int res = 0;
// 如果memories中有记录,则直接return
if (memories[n] != 0) {
return memories[n];
}
if (n == 1 || n == 2) {
res = 1;
}
if (n > 2){
res = process2(n - 1, memories) + process2(n - 2, memories);
}
memories[n] = res;
return res;
}
现在我们将已经已经计算过得结果放在memories中。如果memories中有记录,就不需要再去递归做重复计算。
动态规划
public int fibonaci3(int n) {
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 1;
for (int i = 3; i <= n; i++) {
dp[i] = (dp[i - 1] + dp[i - 2]);
}
return dp[n];
}
动态规划的优化,滚动数组的思想
当然这题的dp数组是一维的,所以我们只需要几个变量来滚动向前就行了。
最小路径和这题的dp数组就是二维的,所以优化的话就是使用一维滚动数组。
public int fibonaci4(int n) {
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
int tmp;
int pre = 1;
int cur = 1;
for (int i = 3; i <= n; i++) {
tmp = cur;
cur = pre + cur;
pre = tmp;
}
return cur;
}
番外篇:快速幂
左神的书中的特别解法,这种解法是用了某些数学定理。定理说F(N)=F(N-1)+F(N-2)是严格的递推公式,所以一定有(F(n), F(n-1)) = (F(n-1), F(n-2)) * [[a, b], [c, d]],状态矩阵: [[a, b], [c, d]]。带入f(1),f(2),f(3),f(4)等,可以求出状态矩阵为{{1, 1}, {1, 0}}。
要求F(n)则变为求矩阵的幂。
而不论求一个数字的幂,还是求矩阵的幂,我们都有一个方法叫快速幂。不会快速幂的同学,可以先看看如何求一个数字的幂,再来看矩阵的,代码基本一样。
// 快速幂求矩阵的幂
public int[][] matrixPower(int[][] m, int p) {
int[][] res = new int[m.length][m[0].length];
// 初始化为单位矩阵
for (int i = 0; i < res.length; i++) {
res[i][i] = 1;
}
int[][] tmp = m;
for (; p != 0; p >>= 1) {
if ((p & 1) == 1) {
res = muliMatrix(res, tmp);
}
tmp = muliMatrix(tmp, tmp);
}
return res;
}
// 矩阵乘法计算
private int[][] muliMatrix(int[][] m1, int[][] m2) {
int[][] res = new int[m1.length][m2[0].length];
for (int i = 0; i < m1.length; i++) {
for (int j = 0; j < m2[0].length; j++) {
for (int k = 0; k < m2.length; k++) {
res[i][j] += m1[i][k] * m2[k][j];
}
}
}
return res;
}
public int fibonaci5(int n) {
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
int[][] res = matrixPower(new int[][]{{1, 1}, {1, 0}}, n - 2);
return 1 * res[0][0] + 1 * res[1][0]; // 这一行不懂的往上看看数学公式。
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库