动态规划——数学模型精解
动态规划是运筹学的一个分支,主要用于求解多阶段决策过程的优化问题。1950年代初,R.E. Bellman提出了最优性原理,将复杂的多阶段问题分解为一系列单阶段问题逐步求解,开创了动态规划这一方法。1957年,他出版了《Dynamic Programming》,成为该领域的经典著作。动态规划自问世以来,在经济管理、生产调度、工程技术及最优控制等领域得到广泛应用,如最短路径、库存管理、资源分配等问题。虽然动态规划主要用于动态过程的优化,但一些静态问题(如线性规划)也可通过引入时间因素来求解。动态规划作为一种解决问题的方法,没有固定的算法或规则,需要针对具体问题进行灵活建模与求解。
一、多阶段决策问题
动态规划是一种求解多阶段决策问题的数学方法。通过将复杂的问题分解为多个相互关联的子问题,并利用其递归结构,动态规划能够有效求解最优化问题。该方法的核心思想是将每个阶段的最优决策作为后续阶段的基础,避免重复计算。
例1:一艘货轮在 A 港装货后驶往 F 港,中途须靠港加油和加淡水 3 次,从 A 港到 F港全部可能的航行路线及两港之间的距离如图所示,F 港有 3 个码头,试求最合理的停靠的码头及航线,使总路程最短。
最短路问题 | 多阶段决策 |
---|---|
![]() |
![]() |
问题是求 A 港到 F 港最短路程,但从 A 港到 F 港不能直接到达,中途须靠港加油和加淡水 3 次,即必须经过 B1、B2,C1、C2、C3,D1、D2 中的一个港口加油和加淡水。这样的问题可看成一个多阶段决策问题,第一阶段考虑货轮到 B1 港还是到 B2 港;第二阶段考虑货轮到 C1 港、C2 港还是到 C3 港?第三阶段考虑货轮到 D1 港还是到 D2港;第四阶段考虑货轮到 F1 港、F2 港还是到 F3 港;依此类推,最后获得货轮从 A 港到 F 港的一条路径。显然这样的路径有很多,问题是选择一条最优路径,使得总的路程最短,这是一个典型的动态规划问题。
例2:资源分配问题。设有6万元资金用于4个工厂的扩建,已知每个工厂的投资收益(万吨)同投资额的大小有关。应如何确定对这4个工厂的投资额,使总投资收益最大?
工厂投资额(i) | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
1 | 0 | 20 | 42 | 60 | 75 | 85 | 90 |
2 | 0 | 25 | 45 | 57 | 65 | 70 | 73 |
3 | 0 | 18 | 39 | 61 | 78 | 90 | 95 |
4 | 0 | 28 | 47 | 65 | 74 | 80 | 85 |
可以把对4个工厂的投资依次看成4个阶段的决策过程,对第i个工厂的投资额看成第i个阶段的决策,i = 1, 2, 3, 4。第一个阶段开始时拥有的资金为6万元,即考虑工厂1投资决策时的资金额是6万元,投资工厂1的资金额为,所以在考虑工厂2投资时剩余的资金额为,依此类推,第4阶段的决策是将剩余资金都投入建厂,这样第5阶段的投资额为0,即。
一般地,阶段的动态规划问题可以描述为下图所示的模式,其中:
- 状态 表示第个阶段开始时的已知决策条件;
- 表示在这种决策环境下第个阶段所采取的决策;
- 选择这个方案获得的收益为;
- 该决策也决定了第个阶段的状态,状态的确定依赖于状态之间的关系方程,其中。
这种描述模式概括了动态规划问题的基本结构,其中每个阶段的决策依赖于当前状态,并影响下一个阶段的状态和可能的收益。
阶段动态规划 | 动态规划的子过程 |
---|---|
![]() |
![]() |
动态规划求解问题的特点
最优子结构:最优解可以通过子问题的最优解组合而成。如果问题的解可以通过分解为较小的子问题来递归解决,并且这些子问题的最优解可以组合为原问题的最优解,那么该问题就具备最优子结构。这是动态规划算法的基本假设。
无后效性:在每个阶段的状态下,当前阶段的决策只依赖于当前状态,而不依赖于之前的决策历史。也就是说,一旦当前的状态确定,之前如何达到这一状态不再重要,系统接下来的演变只与当前状态相关。这一点与“马尔可夫性”类似。
重叠子问题:动态规划适用于那些在求解过程中会反复出现相同子问题的情况。与分治法不同,动态规划会保存已经计算过的子问题的解,避免重复计算,从而提高效率。
二、动态规划的基本概念和数学模型
动态规划是一种用于解决多阶段决策问题的数学方法,特别适合处理具有重叠子问题和最优子结构特性的复杂优化问题。其核心思想是将问题分解为多个相互关联的子问题,通过逐步解决每个子问题,从而得到全局最优解。
2.1 基本概念
- 阶段 (Stage)
阶段是动态规划中将问题解决过程进行自然划分的一个概念。通常基于时间或空间的顺序,在每个阶段,决策者都需要做出一个决策。阶段变量通常用 表示,,其中 代表问题的总阶段数。 - 状态 (State)
状态是动态规划中用于描述每个阶段开始时系统所处的状况的概念。状态变量通常用 表示,其中 是阶段的编号。
-决策 (Decision)
在动态规划的每个阶段,系统处于某个状态,决策者需要根据当前的状态来选择一个最优的决策。决策变量描述了系统在每个阶段的选择,通常记作 。 - 策略 (Policy)
策略是动态规划中由一系列决策组成的一个完整序列。完整的策略可以记作 ,其中 是从初始状态 开始经过 个阶段的所有决策序列。 - 状态转移方程 (State Transition Equation)
状态转移方程是描述系统如何从一个阶段的状态转换到下一个阶段状态的关系式。形式通常为: - 指标函数与最优值函数 (Objective Function & Optimal Value Function)
指标函数用来衡量决策过程的优劣,通常以收益或成本作为标准。最优值函数 表示从第 阶段的状态 开始到过程结束时的最优总收益或最小总成本。
例3:假设有一个背包,能够承受的最大重量是 ,现有若干物品,每个物品有一个重量 和价值 。目标是选择一组物品放入背包,使得背包中的物品总价值最大。
- 阶段:每一个物品对应一个阶段, 阶段表示当前是否考虑第 个物品。
- 状态: 表示在第 阶段时,当前背包中的剩余承重量。
- 决策: 表示在第 阶段是否选择放入物品 ,决策变量是 0 或 1。
- 状态转移方程:如果选择了物品 ,下一个状态就是当前承重量减去物品 的重量 ,即 。
- 指标函数:指标函数是所有被选中物品的总价值,目标是最大化背包的总价值。
通过递归求解上述问题,可以找到使得背包总价值最大化的最优策略,即哪些物品应该被放入背包。
2.2 数学模型
最优化原理:作为整个过程的最优策略具有这样的性质:无论过去的状态和决策如何,相对于前面的决策所形成的状态而言,余下的决策序列必然构成最优子策略。也就是说,一个最优策略的子策略也是最优的。
动态规划方法建模的关键在于正确地写出基本的递推关系式和恰当的边界条件(简称基本方程)。要做到这一点,就必须将问题的过程分成几个相互联系的阶段,恰当的选取状态变量和决策变量及定义最优值函数,从而把一个大问题转化成一组同类型的子问题,然后逐个求解。即从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行,最后一个子问题所得的最优解,就是整个问题的最优解。在多阶段决策过程中,动态规划方法是既把当前一段和未来一段分开,又把当前效益和未来效益结合起来考虑的一种最优化方法。因此,每段决策的选取是从全局来考虑的,与该段的最优选择答案一般是不同的。通常需要按照以下步骤建立模型:
- 划分阶段
划分阶段是动态规划求解的第一步。阶段的划分通常基于时间或空间顺序。每个阶段代表决策过程中的一个步骤。例如,在路径规划问题中,每个阶段可以表示在地图上走到某一位置的时刻;在投资规划中,每个阶段可以表示某一年。
对于静态问题,也可以人为地引入时间概念来划分阶段。将过程分成多个阶段的目的是逐步求解每个子问题,确保递推关系的建立。 - 选择状态变量
状态变量用于描述每个阶段系统所处的状态。状态变量应能全面反映当前阶段的系统情况,并且需要满足无后效性,即当前的状态不依赖于之前的路径。状态变量通常以 表示,表示第 阶段的状态。允许状态的集合称为允许状态集合,记作 。
例如,在背包问题中,状态变量可以选择当前背包的剩余承重量和已经装入背包物品的总价值。 - 确定决策变量及允许决策集合
决策变量是每个阶段需要做出的选择,它直接影响系统从当前状态向下一阶段状态的演变。决策变量 表示在第 阶段,处于状态 时的决策选择。
决策变量的取值范围称为允许决策集合,记作 。它包含所有在当前状态下可行的决策。例如,在路径规划问题中,决策变量可能表示从当前地点出发,向哪个方向行进。 - 建立状态转移方程
状态转移方程描述系统从当前阶段的状态演变到下一阶段的规则。给定当前阶段的状态和决策,系统会演变到下一阶段的状态,状态转移方程通常表示为:
状态转移方程的形式因问题而异。例如,在背包问题中,若决定选择某个物品放入背包,那么下一个阶段的状态就是当前的剩余承重量减去该物品的重量。 - 确定阶段指标函数和最优值函数,建立动态规划基本方程
阶段指标函数用来衡量当前阶段的收益或成本,通常记作 ,它表示在状态 下,做出决策 所获得的收益或成本。
最优值函数 表示从第 阶段状态 开始,系统演变到终止状态所能获得的最大收益或最小成本。根据阶段的递推关系,可以写出动态规划的基本方程:
递推方程最优决策通过比较当前阶段收益与下一阶段的最优值来递归求解的。逆序递推过程自最后阶段向前进行,从而逐步找到整个问题的最优解,顺序递推过程自开始阶段向后进行,从而逐步找到整个问题的最优解。
例4:假设有一个城市网络,网络中的城市通过道路连接,每条道路有一个正数权重,表示两座城市之间的距离。目标是从起点城市(比如 A)到终点城市(比如 E),找到经过的总距离最短的路径。这个问题可以用动态规划来求解。
- 阶段:阶段是指每一次从一个城市到达另一个城市的过程。在这个问题中,阶段的划分可以基于城市的访问顺序。第 阶段表示正在访问网络中的第 个城市。例如,从起点城市 A 开始,经过第一个城市到达第二个城市,直到到达终点 E,每个阶段对应城市的访问顺序。
- 状态:状态描述每个阶段所处的城市。状态变量 表示在第 阶段所处的城市。每个城市对应一个状态,状态的集合就是所有可能访问的城市集合。例如,若 A、B、C、D、E 为五个城市,则状态集合 。其中 表示在第 阶段处于哪个城市,例如 表示第 3 阶段处于城市 C。
- 决策:在每个阶段,给定当前所在城市,决策变量是指下一步选择前往哪个城市。决策变量 表示当前状态 下的决策,即从当前城市前往的下一个城市。允许决策集合是当前城市可以前往的所有邻接城市的集合。例如,如果从城市 A 出发可以前往 B、C,那么决策变量 可以是 B 或 C。
- 状态转移方程:状态转移方程描述了从当前城市(状态)前往下一城市的演变规则。在这个问题中,状态转移方程表示从当前城市 ,通过选择决策 ,到达下一个城市 。假设从城市 到城市 的距离为 ,那么状态转移方程可以表示为:,也就是说,给定当前状态(城市)和决策(选择的下一城市),系统将转移到下一个状态(下一个城市)。
- 阶段指标函数和最优值函数
阶段指标函数 表示从当前城市 出发,通过选择决策 后到达下一城市所消耗的距离。在最短路径问题中,阶段指标函数就是两座城市之间的距离。最优值函数 表示从城市 开始,到达终点城市所能走的最短距离。根据递推关系,动态规划的基本方程可以写为:,其中, 表示当前阶段的距离,而 表示从下一城市到终点的最短路径。
三、动态规划的Python求解
3.1 示例1
例5:一家公司有1000辆运输卡车,要求在未来5年制定一个分配方案,以在高负荷(每天行驶500km)和低负荷(每天行驶300km)运输之间平衡卡车的分配,从而最大化公司的利润。高负荷与低负荷运输条件下的年利润和年损坏率不同,应用动态规划来解决这个多阶段决策问题。
动态规划数学模型
- 阶段划分
问题分为5个阶段,每个阶段代表一年。每年公司可以决定有多少辆车进行高负荷运输,剩下的车则进行低负荷运输。 - 状态变量
状态变量 表示在第 年剩余的完好卡车数量。初始状态为 ,表示公司开始时有1000辆完好的卡车。 - 决策变量
决策变量 表示在第 年选择进行高负荷运输的卡车数量,剩余的 辆车将进行低负荷运输。 - 状态转移方程
卡车在一年中的损坏率不同,状态转移方程为:,表示在第 年结束时剩下的完好卡车数量。 - 收益函数
每年的总利润为高负荷和低负荷运输的总收益之和: - 数学模型
- 表示从第年开始,剩余辆完好卡车时的最大利润。
- 递推关系:
其中,表示经过一年后剩余的完好卡车数量。
from mip import Model, xsum, maximize, INTEGER
def optModel(m, n, g, h, a, b):
# 使用CBC求解器
model = Model("truck_load_distribution", solver_name="CBC")
# 定义变量:x1代表高负荷运输的卡车数量,x2代表低负荷运输的卡车数量
x1 = [model.add_var(var_type=INTEGER) for i in range(n)] # 高负荷运输卡车数量
x2 = [model.add_var(var_type=INTEGER) for i in range(n)] # 低负荷运输卡车数量
# 设置目标函数,最大化5年内的总利润
model.objective = maximize(xsum(x1[i] * g + x2[i] * h for i in range(n)))
# 初始条件:第一年初的卡车总数为1000辆
model += x1[0] + x2[0] == m
# 状态转移方程:每一年剩余的卡车数量
for i in range(1, n):
model += x1[i] + x2[i] == x1[i - 1] * a + x2[i - 1] * b
# 运行优化求解
model.verbose = 0
model.optimize()
# 输出优化结果:最大利润和每年高、低负荷运输的卡车分配
print("5年内的最大总利润:", model.objective_value)
lis1 = []
lis2 = []
for i in range(n):
lis1.append(int(x1[i].x))
lis2.append(int(x2[i].x))
print("每年高负荷运输的卡车数量:", lis1)
print("每年低负荷运输的卡车数量:", lis2)
# 修改为提供的参数
if __name__ == '__main__':
# 参数:初始卡车数量、年份、每辆车的高负荷利润、低负荷利润、高负荷损坏率、低负荷损坏率
m = 1000 # 初始卡车数量
n = 5 # 5年
high_profit = 25 # 高负荷运输的年利润(万元)
low_profit = 16 # 低负荷运输的年利润(万元)
high_damage = 0.3 # 高负荷运输的年损坏率
low_damage = 0.1 # 低负荷运输的年损坏率
# 调用优化模型
optModel(m, n, high_profit, low_profit, 1 - high_damage, 1 - low_damage)
5年内的最大总利润: 74740.0
每年高负荷运输的卡车数量: [0, 0, 795, 570, 399]
每年低负荷运输的卡车数量: [1000, 900, 15, 0, 0]
3.2 示例2
例6:有一个背包,它的容量是 单位。现在有 5 种不同的物品,每种物品都有其重量和价值,求如何选择物品,使得背包中物品的总价值最大,且不超过背包的容量。
物品编号 | 物品1 | 物品2 | 物品3 | 物品4 | 物品5 |
---|---|---|---|---|---|
重量(单位) | 3 | 4 | 5 | 8 | 7 |
价值(单位) | 25 | 20 | 15 | 40 | 30 |
建模过程
- 阶段(Stage):阶段可以理解为每次决策选择物品的过程。这里总共有 5 个物品,因此我们有 5 个阶段,每个阶段对应我们是否选择当前物品放入背包。第 1 阶段对应物品 1,依此类推,直到第 5 阶段(物品 5)。
- 状态(State):状态表示背包的剩余容量。对于每个阶段 ,状态 可以表示背包当前还剩下多少容量。从初始阶段开始,背包的容量是 15(即 ),然后根据选择是否放入物品,状态会逐步更新。
- 决策(Decision):决策是指在每个阶段我们是否选择将物品放入背包。决策变量可以定义为:
- :表示第 阶段选择放入物品 ;
- :表示第 阶段不放入物品 。
- 状态转移方程(State Transition Equation):状态转移方程描述的是在每个阶段中,根据当前状态和决策,背包容量如何变化。假设当前背包剩余容量为 ,如果我们选择将物品 放入背包(即 ),那么下一阶段的状态 就会变为 ,即减去物品 的重量。如果我们不选择放入物品 (即 ),则状态不变。
- 阶段指标函数:每个阶段的指标函数表示将物品放入背包时所获得的价值(如果选择了该物品)。对于阶段 ,其指标函数可以表示为:
- 最优值函数:最优值函数 表示从第 阶段状态为 时,背包可以获得的最大总价值。
递推方程可以写为:
这意味着在第 阶段,有两种选择:
- 不选择物品 ,即状态不变,下一阶段仍然有 的容量可用,此时获得的总价值是 ;
- 选择物品 ,即状态减少物品 的重量,下一阶段的剩余容量为 ,此时获得的总价值是 。
如果当前背包容量不足以容纳物品 (即 ),则我们不能选择该物品,即只能走第一条路径。 - 边界条件:对于最后一个阶段(即阶段 5)和每个阶段背包容量为 0 的情况,最优值函数的值为 0,即:
可以从第 5 阶段开始递推,直到第 1 阶段,以找到背包的最优解。
- 对于第 5 阶段:
- 如果背包剩余容量大于或等于物品 5 的重量(即 ),我们可以选择是否放入物品 5;
- 如果选择放入,获得价值 30,状态变为 ;
- 如果不放入,则状态不变,收益为 0。
- 对于第 4 阶段:
- 如果背包剩余容量大于或等于物品 4 的重量(即 ),我们可以选择是否放入物品 4;
- 如果选择放入,获得价值 40,状态变为 ;
- 如果不放入,则状态不变,收益为 0。
依此类推,直到第 1 阶段。
from mip import Model, xsum, maximize, INTEGER
# 初始化参数
n = 5 # 物品数量
weights = [3, 4, 5, 8, 7] # 每个物品的重量
values = [25, 20, 15, 40, 30] # 每个物品的价值
W = 15 # 背包容量
# 构建模型,指定使用 CBC 求解器
model = Model("knapsack_problem", solver_name="CBC")
# 定义变量:x[i] 表示物品 i 是否被选中(0 或 1)
x = [model.add_var(var_type=INTEGER, lb=0, ub=1) for i in range(n)]
# 目标函数:最大化背包中物品的总价值
model.objective = maximize(xsum(values[i] * x[i] for i in range(n)))
# 约束条件:背包中物品的总重量不能超过 W
model += xsum(weights[i] * x[i] for i in range(n)) <= W
# 求解模型
model.optimize()
# 输出最优解
print(f"最优总价值: {model.objective_value}")
print("选中的物品编号为:")
for i in range(n):
if x[i].x >= 0.99: # 如果 x[i] 为 1,则表示物品 i 被选中
print(f"物品 {i+1}")
最优总价值: 85.0
选中的物品编号为:物品 1;物品 2;物品 4
总结
动态规划是一种通过将复杂问题分解为多个相互关联的子问题,从而逐步求解全局最优解的数学方法。其核心思想是利用递推关系,通过解每个子问题来最终解决原问题。动态规划的基本要素包括以下几方面:
阶段:动态规划将问题划分为多个阶段,每个阶段代表问题解决过程中的一个步骤。每个阶段的结果会影响下一阶段的决策。
状态:状态是每个阶段所处的具体情况。每个状态能够总结之前的决策过程,描述系统在该阶段的特定情形。
决策:在每个阶段中,针对当前状态,决策者需要选择最佳的行动(决策)来影响下一阶段的状态。
策略:策略是指由一系列决策组成的序列,从初始状态开始一直到最终状态,动态规划通过分析不同策略来找到最优解。
状态转移方程:状态转移方程描述了在做出某个决策后,系统如何从一个状态转移到下一个状态。它是动态规划的递推基础。
指标函数:指标函数衡量决策的优劣,通常是与目标相关的收益、成本或效用。动态规划的目标是最大化(或最小化)该函数的值。
动态规划广泛应用于多领域优化问题,如路径规划(最短路径问题)、资源分配(如企业资源优化)、背包问题(选择最优的物品组合以最大化收益)等。它通过记忆子问题的最优解,避免了重复计算,提高了求解效率。尤其在解决具有重叠子问题和最优子结构的问题时,动态规划展现出显著的优势。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!