解锁动态规划:从斐波那契到高效算法

动态规划(Dynamic Programming, DP)是解决优化问题的一种算法策略,它将一个复杂问题分解为更小的子问题,通过解决子问题来逐步找到复杂问题的最优解。动态规划适用于有重叠子问题和最优子结构性质的问题。接下来,我们通过一个经典的动态规划问题——斐波那契数列(Fibonacci Sequence)来详细介绍动态规划的思路和实现步骤。

问题定义

斐波那契数列是这样一个序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …,其中每个数字(从第三个数字起)都是前两个数字的和。斐波那契数列的定义如下:

我们的目标是编写一个函数,输入n,输出斐波那契数列的第n项。

1. 递归解法(非DP解)

首先,我们尝试使用递归解法,这种方法简单直观,但效率较低。

def fibonacci_recursive(n):if n <= 1:return nelse:return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)

这个递归解法虽然简单,但它进行了大量重复计算,时间复杂度为O(2^n)。 

我们可以通过分析斐波那契数列的递归树来直观地看出重复计算的发生。

斐波那契数列的递归树

考虑计算F(5)的过程,递归树如下所示:

在这个树状结构中,每个节点表示一个递归调用,节点的值是调用的参数。从这个树中我们可以观察到:

  1. 重复的子问题F(3)F(2)F(1)F(0)被计算了多次。特别是F(2),在整个计算过程中被重复计算了三次。

  2. 增长的递归调用:随着参数n的增加,递归调用的数量呈指数级增长。例如,F(n)的计算会导致F(n-1)F(n-2)的计算,而这些调用又会分别导致更多的递归调用。

分析重复计算的原因

重复计算的主要原因在于,递归解法中对每个子问题的解决都是独立进行的,它没有考虑到在求解过程中,很多子问题实际上是相同的。由于递归方法没有记录已经解决的子问题的结果,每次遇到这些子问题时,它都会重新进行计算。

2. 动态规划解法

为了提高效率,我们使用动态规划的方法,避免重复计算。

步骤1:定义状态

定义dp数组,其中dp[i]表示斐波那契数列中第i个数的值。

步骤2:确定状态转移方程

根据斐波那契数列的定义,我们可以得到状态转移方程:dp[i] = dp[i-1] + dp[i-2]

步骤3:确定初始条件和边界条件

dp[0] = 0, dp[1] = 1

步骤4:计算顺序

从小到大计算dp数组的值。

动态规划代码实现
def fibonacci_dp(n):if n <= 1:return ndp = [0] * (n+1)dp[1] = 1for i in range(2, n+1):dp[i] = dp[i-1] + dp[i-2]return dp[n]

这种方法的时间复杂度为O(n),空间复杂度也为O(n)。

3. 动态规划优化

对于斐波那契数列问题,我们实际上不需要保存整个dp数组,只需要保存前两个状态即可。

优化后的代码
def fibonacci_dp_optimized(n):if n <= 1:return nprev, curr = 0, 1for i in range(2, n+1):prev, curr = curr, prev + currreturn curr

这个优化后的版本将空间复杂度降低到了O(1)。

4. 算法思考

1. 子问题

在动态规划中,我们将原问题分解为较小的、相互关联的子问题。对于斐波那契数列问题,求F(n)可以看作是一个原问题,它可以分解为求F(n-1)F(n-2)两个子问题。而F(n-1)F(n-2)又可以继续分解,直到F(1)F(0)

2. 重叠子问题

动态规划适用于那些有大量重叠子问题的情况,即不同的问题求解路径中包含了许多相同的子问题。在递归求解斐波那契数列时,F(n-1)F(n-2)会重复计算F(n-3)F(n-4)等更小的子问题。动态规划通过记忆化(存储)这些子问题的解来避免重复计算。

3. 最优子结构

斐波那契数列问题具有最优子结构的特点,即问题的最优解包含了其子问题的最优解。虽然斐波那契数列问题本身不涉及“最优化”,但其解决方法体现了最优子结构的思想:F(n)的最优解(即准确解)可以通过组合F(n-1)F(n-2)的解得到。

4. 动态规划表(DP Table)

在这个例子中,dp数组就是所谓的动态规划表。它用来记录每一步的结果(即每个F(i)的值),以便于后续的计算可以直接引用前面的结果,而不是重新计算。这种方法极大地提高了效率,因为每个子问题只被解决一次,并且一旦被解决,其结果就会被保存。

5. 状态转移方程

动态规划的核心是状态转移方程。在斐波那契数列的例子中,状态转移方程是dp[i] = dp[i-1] + dp[i-2]。这个方程描述了问题状态之间的关系,即如何从已知的子问题的解得到当前问题的解。

通过这个斐波那契数列的例子,你可以看到,尽管我们使用的是简单的数组来存储每一步的结果,但这个过程完全体现了动态规划的思想:分解问题、解决子问题、存储子问题的解以避免重复计算。这就是dp数组与动态规划思想关联的方式,它是实现动态规划策略的一个工具。

总结

动态规划中的应用体现了动态规划的核心思想:存储中间结果,避免重复计算。这里的dp数组正是用来存储每个子问题的解,即斐波那契数列中每个位置的数值。让我们更深入地理解它与动态规划思想的关联:

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

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

相关文章

基于SSM的药店药品商城管理系统

介绍 本项目分为前后台&#xff0c;分为管理员与普通用户两种角色&#xff0c;管理员登录后台&#xff0c;普通用户登录前台&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,订单管理,客户管理,药品管理,类目管理等功能。用户角色包含以下功能&#xff1a; 用户首…

pycharm打包python文件为exe文件(图文教程)

1.安装pyinstaller库 pip3 install pyinstaller 2.使用pyinstaller 打包文件 首先确保Terminal命令行中&#xff0c;你的路径和你的项目是同一个路径 我的项目就是放在golden_dev中的。 3.命令行内输入打包代码 pyinstaller -F -w gold_miner.py gold_miner.py 是我的项目…

高斯消元详解

算法概述 高斯消元法是一个用来求解线性方程组的算法 那么什么是线性方程组呢? 线性:每个未知数次数都为1次方程组:多个方程&#xff0c;多个未知数。 &#xff08;a1x1a2x2..anxnbn&#xff09;x为一次的 当x是平方的时候就不是线性 简而言之就是有多个未知数&#xff…

0基础学习Mybatis系列数据库操作框架——多环境配置

大纲 配置代码参考资料 在实际开发中&#xff0c;我们往往会将开发环境分成&#xff1a;开发、测试、线上等环境。这些环境的数据源不一样&#xff0c;比如开发环境就不能访问线上环境&#xff0c;否则极容易出现线上数据污染等问题。Mybatis通过多环境配置分开定义来解决这个问…

Shell脚本之基本语法

目录 一、变量定义 变量命名规则&#xff1a; 变量的赋值&#xff1a; 只读变量&#xff1a; 删除变量&#xff1a; 二、变量的类型 自定义变量&#xff1a; 环境变量&#xff1a; 位置参数&#xff1a; 预定义变量&#xff1a; 三、键盘输入 四、数值运算 为什么…

数据结构—堆

什么是堆 堆是一种特殊的树形结构&#xff0c;其中每个节点都有一个值。堆可以分为两种类型&#xff1a;最大堆和最小堆。在最大堆中&#xff0c;每个节点的值都大于等于其子节点的值&#xff1b;而在最小堆中&#xff0c;每个节点的值都小于等于其子节点的值。这种特性使得堆…

RPA自动化小红书自动化写文以及发文!

1、视频演示 RPA自动化小红书自动写作发文 2、核心功能点 采集笔记&#xff1a;采集小红书上点赞量大于1000的爆款笔记 下载素材&#xff1a;下载爆款笔记的主图 爆款改写&#xff1a;根据爆款笔记的标题仿写新的标题以及新的文案 自动发布&#xff1a;将爆款笔记发布到小红…

Three.js——scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

第N6周:使用Word2vec实现文本分类

import torch import torch.nn as nn import torchvision from torchvision import transforms,datasets import os,PIL,pathlib,warnings #忽略警告信息 warnings.filterwarnings("ignore") # win10系统 device torch.device("cuda"if torch.cuda.is_ava…

基于springboot+vue+Mysql的招生管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

震惊!!原来阻塞队列消息队列这样理解会更简单!!!

震惊!!原来阻塞队列&&消息队列这样理解会更简单!!! 一:阻塞队列二:消息队列2.1:生产者消费者模型2.1.1:解耦合:2.1.2:削峰填谷: 三:消息队列代码3.1.13.1.2:3.1.3:生产慢,消费快,消费阻塞3.1.3:生产快,消费慢,生产阻塞 二级目录二级目录 一:阻塞队列 阻塞队列:先进先出…

gitcode 配置 SSH 公钥

在 gitcode 上配置SSH公钥后&#xff0c;可以通过SSH协议安全地访问远程仓库&#xff0c;无需每次都输入用户名和密码。以下是配置SSH公钥的步骤&#xff1a; 5分钟解决方案 用 OpenSSH公钥生成器 生成 公钥和私钥&#xff0c;私钥文件&#xff08;id_rsa&#xff09;下载&am…

【Leetcode】top 100 图论

基础知识补充 1.图分为有向图和无向图&#xff0c;有权图和无权图&#xff1b; 2.图的表示方法&#xff1a;邻接矩阵适合表示稠密图&#xff0c;邻接表适合表示稀疏图&#xff1b; 邻接矩阵&#xff1a; 邻接表&#xff1a; 基础操作补充 1.邻接矩阵&#xff1a; class GraphAd…

Open3D(C++) 鲁棒损失函数优化的ICP算法

目录 一、损失函数1、关于2、损失函数3、Open3D实现二、代码实现三、结果展示1、配准前1、配准后本文由CSDN点云侠原创,

C语言----数据在内存中的存储

文章目录 前言1.整数在内存中的存储2.大小端字节序和字节序判断2.1 什么是大小端&#xff1f;2.2 练习 3.浮点数在内存中的存储3.1.引子3.2.浮点数的存储3.2.2 浮点数取的过程 前言 下面给大家介绍一下数据在内存中的存储&#xff0c;这个是一个了解c语言内部的知识点&#xf…

【Linux学习】Linux 的虚拟化和容器化技术

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

MySQL 导入库/建表时/出现乱码

问题描述&#xff1a; 新建不久的项目在使用Navicat for MySQL进行查看数据&#xff0c;发现表中注释的部分乱码&#xff0c;但是项目中获取的数据使用不会。 猜测因为是数据库编码和项目中使用的不一样&#xff0c;又因为项目的连接语句定义了需要编码&#xff0c;故项目运行…

浅述安防视频监控平台EasyCVR视频汇聚管理系统运维管理能力

智慧安防监控EasyCVR视频管理平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。国标GB28181协议视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、…

在云端遇见雨云:一位服务器寻觅者的指南

引言&#xff1a;寻觅一座云端归宿 当我踏入数字世界的边缘&#xff0c;带着对网络的探索与期待&#xff0c;我迫切需要一座安全可靠的数字栖息地。云计算技术正如一场魔法般的变革&#xff0c;而在这片广袤的云端中&#xff0c;雨云就像是一位友善的向导&#xff0c;引领我穿越…

30.多个线程交替执行

线程一输出a,5次&#xff1b; 线程二输出b,5次&#xff1b; 线程三输出c,5次&#xff1b; 现在要求输出abcabcabcabcabc怎么实现&#xff1f; 采用wait和notifyAll实现 public class ThreadTest {public static void main(String[] args) {WaitNotify waitNotify new Wai…