首页 > 编程语言 >动态规划——经典问题的实现(Python)

动态规划——经典问题的实现(Python)

时间:2023-04-14 12:13:40浏览次数:60  
标签:状态 Python 决策 问题 阶段 经典 动态 规划

动态规划(dunamic programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。将复杂的多段决策问题分解为若干相互关联的子决策问题以获得最优决策序列的方法,是由美国数学家贝尔曼(R.E.Bellman)于20世纪50年代提出,它的基本原理是贝尔曼在《动态规划》(1957年)一书中所提出的最优化原理,即多段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成某一状态而言,余下的所有决策总构成一个最优策略。动态规划应用及其广泛,包括工程技术、经济、工业产业、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。动态规划在经济管理、生产调度、工程技术和最有控制等方面得到了广泛应用。例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其他方法求解更方便。

一、序贯决策—多阶段决策

在现实生活中,有一类活动的过程,由于他的特殊性,可将过程分成若干个互相联系的阶段,在他的每个阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,他依赖与当前面临的状态,又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线,这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策问题。在多阶段决策问题中,各个阶段采取的决策一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化的过程为动态规划方法。
多阶段决策问题中,各个阶段采用的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有动态的含义,称这种解决多阶段决策最优化问题的方法为动态规划方法。

在解决问题的过程中,需要经历多个阶段。每个决策阶段产生一组状态,最终通过某组决策序列,得出最优解。使用动态规划的问题必须满足最优化原理和无后效性。

1.1 无后效性

将各阶段按照一定的次序列排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,无后效性。

1.2 结构自相似性

如果一个问题最优解包括规模更小相同问题的最优解,就可以用动态规划来解决。动态规划结构的自相似性是阶段划分的依据,本质上是动态规划问题结构上的自相似性。

1.3 子问题的重叠性

动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,他在实现的过程中,不得不存储产生过程中的各种状态,所以他的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上无法承受,所以我们舍空间而取时间。
动态规划的数学模型。根据决策过程的演变是确定性的还是随机性的。可分为确定性决策过程和随机性决策过程。另外,也可按照时间参量是离散的或是连续的变量。可分为离散决策过程和连续决策过程。组合起来就有离散确定性、离散随机性、连续确定性、连续随机性四种决策过程模型。对于确定性的决策过程。问题中的下一段的状态已由当前段的状态及决策完全确定。对于随机性决策过程,他与确定性决策过程的区别在于下一段的状态并不能由当前段的状态及决策完全确定,而是按某种概率分布来决定下一段的状态。这种概率分布是由当前段的状态和策略完全确定。

二、动态规划基本思想

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解为若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解决的子问题的答案。不管该子问题以后是否被用到,只要他被计算过,就将其结果填入表中,这就是动态规划的基本思路。具体的动态规划算法多种多样,到那时他们具有相同的填表格式。
** 最优化原理可以这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略,简言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。**

三、动态规划的术语

多阶段决策问题:如果一类活动过程可以分为若干个互相联系的阶段,在每一个阶段都需做出决策(采取措施),一个阶段的决策确定以后,常常影响到下一个阶段的决策,从而就完全确定了一个过程的活动路线,则称为多阶段决策问题。各个阶段的决策构成一个决策序列,称为一个策略。每一个阶段都有若干个决策可供选择,因而有许多策略供我们选取,对应于一个策略可以确定活动的效果,这个效果可以用数量来确定,策略不同,效果也不同,多阶段策略问题就是要在可以选择那些策略中间选取一个最优策略,使在预定的标准下达到最好的效果。

阶段:把所给求解问题的过程恰当地分成若干个相互联系的阶段,以便求解,过程不同,阶段数就可能不同,描述阶段的变量称为阶段变量。在多数情况下,阶段变量是离散的,用k表示,此外,也有阶段变量是连续的情形。如果过程可以在任何时刻作出决策,且在任意两个不同时刻之间允许有无穷多个决策时,阶段变量就是连续的。
状态:状态表示每个阶段开始面临的自然状态或客观条件,它不以人们的主观意志为转移,也称为不可控因素。在上面的例子中状态就是某阶段的出发位置,它既是该阶段某路的起点,同时又是前一阶段某支路的终点。
决策:一个阶段的状态给定以后,从该状态演变到下一阶段某个状态的一种选择(行为)称为决策。在最优控制中,也称为控制。在许多问题中,决策可以自然而然地表示为一个数或一组数。不同的决策对应着不同的数值,描述决策的变量称决策变量,因状态满足无后效性,故在每一个阶段选择决策时只需考虑当前的状态而无需考虑过程的历史。决策变量的范围称为允许决策集合。
策略:由每个阶段的决策组成的序列称为策略。对于每一个实际的多阶段策略过程,可供选取的策略有一顶的范围限制,这个范围称为允许策略集合,允许策略集合中达到最优效果的策略称为最优策略。
状态转移方程:给定k阶段状态变量x(k)的值后,如果这一阶段的决策变量一经确定,第k+1阶段的状态变量x(k+1)也就完全确定,即x(k+1)的值随x(k)和第k阶段的决策u(k)的值变化而变化,那么可以把这一关系看成x(k),u(k)与x(k+1)确定的对应关系,用x(k+1)=Tk(x(k),u(k))表示。这是从k阶段到k+1阶段的状态转移规律,称为状态转移方程。
最优化策略函数:作为整个过程的最优策略函数,它满足:相对前面决策所形成的状态而言,余下的子决策必然构成“最优子策略”。最优性原理实际上要求问题的最优策略的子策略也是最优。

四、经典的动态规划问题

4.1 背包问题

**例1:一位旅行者携带背包去登山,已知他所能承受的背包重量限度为\(a(kg)\),现有\(n\)种物品可供他选择装入背包,第\(i\)种物品的单件重量为\(a_i(kg)\),其价值(可以是表明本物品对登山的重要性的数量指标)是携带数量\(x_i\)的函数\(v(x_i)(i=1,2,...,n)\),问旅行者应如何选择携带各种物品的件数,以使总价值最大?

#背包问题4件商品
weights = [2, 3, 4, 5]  # 每个物品的重量
values = [3, 4, 5, 6]  # 每个物品的价值
max_weight = 8  # 背包的最大承重

def knapsack(weights, values, max_weight):
   n = len(weights)  # 获取物品的数量
   dp = [0] * (max_weight + 1)  # 初始化动态规划数组,dp[j]表示承重为j时的最大总价值
   
   for i in range(n):  # 遍历每个物品
       for j in range(max_weight, weights[i]-1, -1):  # 逆序遍历每个承重
           if j >= weights[i]:  # 如果当前物品的重量不超过当前承重j
               dp[j] = max(dp[j], dp[j-weights[i]] + values[i])  # 选择放入物品i或不放入物品i
           else:  # 如果当前物品的重量超过当前承重j
               dp[j] = dp[j]  # 不放入物品i
       
   return dp[max_weight]  # 返回最大总价值

max_value = knapsack(weights, values, max_weight)  # 求解最大总价值
print(max_value)  

背包装的最大价值为10。

4.2 资源配置问题

例2:某集团进行技术升级换代,拟购进将某种高效率机器5台,分配给下属甲、乙、丙三厂,各工厂使用这些设备以后可以产生的效益如下表。问这5台机器如何分配,集团所获收益为最大?

机器数
1 3 5 4
2 7 10 6
3 9 11 11
4 12 11 12
5 13 11 12
import numpy as np
#资源离散分配问题
mat = np.array([[0,0,0,],
                [3,5,4],
                [7,10,6],
                [9,11,11],
                [12,11,12],
                [13,11,12]])
mat_new = mat.copy()
m = mat.shape[0]
n = mat.shape[1]
bestChoice = np.zeros((m,n)).astype(int)
for k in range(n-1):
    for i in range(m):
        max = 0
        for j in range(i+1):
            if mat[j][n-2-k] + mat_new[i-j][n-1-k] > max:
                max = mat[j][n-2-k] + mat_new[i-j][n-1-k]
                bestChoice[i][n-2-k] = j
                bestChoice[i][n-1-k] = i-j
        mat_new[i][n-2-k] = max              #根据算出来的每一阶段的最优价格不断更新价格表与最佳选择表
print("最大盈利为:")
print(mat_new[m-1][0])
print("最佳分配方案为:")
print(bestChoice[m-1][0])
for i in range(n-2):
    print(bestChoice[m-1][i+1] - bestChoice[m-1][i+2])
print(bestChoice[-1][-1])

分配方案:甲0台,乙2台,丙3台。

4.3 最短路径问题

已知 6个城市之间的机票票价(单位:100元)如矩阵所示,求城市 A 到其它城市的票价最便宜的路径及票价。其中若值为0代表两地之间无直航,若值不为零,表示票价。

A B C D E F
A 0 50 0 40 25
B 50 0 15 20 0
C 0 15 0 10 20
D 40 20 10 0 10
E 25 0 20 10 0
import networkx as nx
# 定义机票票价矩阵
costs = [[0, 50, 0, 40, 25, 10],
         [50, 0, 15, 20, 0, 25],
         [0, 15, 0, 10, 20, 0],
         [40, 20, 10, 0, 10, 25],
         [25, 0, 20, 10, 0, 55],
         [10, 25, 0, 25, 55, 0]]
# 将矩阵转换成带权有向图
G = nx.DiGraph()
for i in range(len(costs)):
    for j in range(len(costs[0])):
        if costs[i][j] != 0:
            G.add_edge(chr(65+i), chr(65+j), weight=costs[i][j])

# 计算城市A到其它城市的最短路径及票价
shortest_path = nx.shortest_path(G, source='A', weight='weight')
shortest_path_cost = nx.shortest_path_length(G, source='A', weight='weight')
for dest, cost in shortest_path_cost.items():
    print(f"从A到{dest}的最便宜路径为{shortest_path[dest]}, 票价为{cost}")
从A到A的最便宜路径为['A'], 票价为0
从A到F的最便宜路径为['A', 'F'], 票价为10
从A到E的最便宜路径为['A', 'E'], 票价为25
从A到B的最便宜路径为['A', 'F', 'B'], 票价为35
从A到D的最便宜路径为['A', 'F', 'D'], 票价为35
从A到C的最便宜路径为['A', 'E', 'C'], 票价为45

总结

动态规划算法(Dynamic Programming)是一种高效的算法思想,主要用于解决优化问题。其核心思想是将复杂的问题拆分成多个子问题,然后通过已经求解过的子问题的解来推导出当前问题的解。动态规划算法常常应用于具有重叠子问题和最优子结构性质的问题中。其中,重叠子问题指的是在问题的求解过程中,会重复地求解相同的子问题;最优子结构指的是问题的最优解可以由子问题的最优解推导出来。动态规划对于解决多阶段决策问题的效果是明显的,但是动态规划也有一定的局限性。首先,他没有统一的处理方式,必须根据问题的各种性质并结合一定的技巧来处理;另外当变量的维度增大时,总的计算量及存储量急剧增大。因而,受计算机的存储量及计算速度的限制,当今的计算机仍不能用动规划来解决较大规模的问题,这就是“维数障碍”。

参考文献

  1. 动态规划算法
  2. 动态规划–资源离散分配(PYTHON代码)
  3. 图论之最短路径算法及Python实现

标签:状态,Python,决策,问题,阶段,经典,动态,规划
From: https://www.cnblogs.com/haohai9309/p/17317257.html

相关文章

  • python3语法
    1、编码默认情况下,Python3源码文件以UTF-8编码,所有字符串都是unicode字符串。指定不同编码:#-*-coding:cp-1252-*-2、标识符(1)首字符必须是字母或下划线(2)标识符其他字符由数字、字母和下划线组成(3)标识符对大小写敏感(4)Python3中,可以用中文作为变量名,非......
  • Python学习笔记一:列表
    一.列表1.定义列表,是由一系列按照特定顺序排列的元素组成的一个有序集合。其中可以包含字母,数字,或者其他任何元素,每一个元素之间不一定有关系。不过,在创建列表时,建议还是将相同类型的元素或者相互之间有关联的元素放在一个列表中。鉴于包含的元素的数量,通常在给列表......
  • 使用Spring的getBeansOfType实现接口多实现类的动态调用
     背景org.springframework.beans及org.springframework.context这两个包是SpringIoC容器的基础,其中重要的类有BeanFactory,BeanFactory是IoC容器的核心接口,其职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖关系。ApplicationContext作为BeanFactory的子类......
  • MATLAB代码:基于条件风险价值的合作型Stackerlberg博弈微网动态定价与优化调度
    MATLAB代码:基于条件风险价值的合作型Stackerlberg博弈微网动态定价与优化调度注意:店主有大量P2P分布式交易以及纳什议价的代码,欢迎咨询关键词:微网优化调度条件风险价值合作博弈纳什谈判参考文档:《AcooperativeStackelberggamebasedenergymanagementconsideringpric......
  • Python+Requests+Pytest接口自动化测试微信接口实例
         pytest.ini配置文件[pytest]log_cli=truelog_level=NOTSETlog_format=%(asctime)s%(levelname)s%(message)slog_date_format=%Y-%m-%d%H:%M:%Saddopts=-vs--alluredir./temp-m'file'log_file=./log/test.loglog_file_level=infol......
  • Python中re.finditer函数的使用
    re模块简介re模块是Python标准库中的正则表达式模块。正则表达式是一种特殊的字符串处理方式,常用于匹配文本中的特定模式。re模块可以提供针对正则表达式的支持。re.finditer()函数re.finditer(pattern,string,flags=0)函数功能:扫描整个字符串,并返回对每个匹配项的迭......
  • 用python和批处理命令实现Markdown内嵌图片
    img.py代码如下importbase64fromPILimportImage,ImageGrabimg_name="C:\\Users\\Lenovo\\Desktop\\grab_clipboard.png"#获取并保存剪贴板图片im=ImageGrab.grabclipboard()ifisinstance(im,Image.Image):#print("Image:size:%s,mode:%......
  • Python与c语言的区别与联系
    Python与c语言都是一种机器学习语言,进过长时间的学习和总结,我将Python与c语言的一些特点总结成以下几点,不全面还望多多指正。1、因为C语言是编译型语言,python是解释型语言,所以python的执行速度没有C语言那么快。2、基本元素的区别,python中的基本元素相比于C语言大大减少,比较特殊......
  • 动态规划:剑指 Offer 14- I. 剪绳子
    题目描述:给你一根长度为n的绳子,请把绳子剪成整数长度的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1]...k[m-1]。请问k[0]*k[1]*...*k[m-1]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。  提......
  • Python 之操作redis
    一、示例代码importredispool=redis.ConnectionPool(host='127.0.0.1',port=6379,password="",max_connections=10)redis_obj=redis.Redis(connection_pool=pool,decode_responses=True)#操作字符串redis_obj.set(name="password",valu......