动态规划-初篇

引言:最优化问题

什么是最优化问题?

假设一个问题可以表示为一个目标函数,其输入是可行解(可行解由问题的约束规定),输出是可行解的质量刻画(可行解是否足够好),一般求目标函数的最小值或最大值。

举例1

已知 x 1 + x 2 + x 3 = 10 , 且 x 1 , x 2 , x 3 ∈ N + x_1+x_2+x_3=10,且x_1,x_2,x_3∈N+ x1+x2+x3=10,x1,x2,x3N+,求 g ( x 1 , x 2 , x 3 ) = x 1 x 2 x 3 g(x1,x2,x3)=x_1x_2x_3 g(x1,x2,x3)=x1x2x3的max,在这个问题中目标函数是g,约束条件 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3是正整数且和为10,所有满足约束条件的 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3都是可行解,但使得g最大的可行解为最优解,最优化问题就是找这样的最优解。

动态规划与分治

如果将分治的方法应用于最优化问题,与原始相比,有以下两点不同:

  • 将原问题分解为子问题的时候
    在不知道如何分解才能获得最优的情况下,我们不能像以前一样简单地按下标、值或者区域划分,必须要枚举所有可能的划分方案,通过比较获得最优的划分方案
  • 用子问题的解组成原问题的解的时候
    不像以往直接将子问题的可行解组合,这里需要通过子问题的最优解组合得到原问题的最优解

假设我们能够做到将子问题的最优解组合成原问题的最优解(最优子结构性质),那么就可以应用分治的思想将一个复杂问题的最优化转换成简单小问题的最优化,从而解决问题,这就是动态规划。

多步决策:一步一步的,每一步都做出一个最优的决策。

矩阵链式相乘问题

问题描述

输入:给定n个矩阵,求 A 1 A 2 . . . A n A_1A_2...A_n A1A2...An ,其中 A i A_i Ai的规模是 p i − 1 p_{i−1} pi1行 x p i p_i pi列。
输出:使用括号将乘积 A 1 A 2 . . . A n A_1A_2...A_n A1A2...An完全括起来,求其最小乘法运算次数。

问题分析

如下图,矩阵相乘的顺序虽然不会影响计算结果,但是会影响做乘法运算的次数。所以这个问题变成求最小乘法次数的矩阵相乘的计算顺序。
在这里插入图片描述
最优化问题是从所有可行解中找出最优,所以我们需要所有可行解的个数。我们设n个矩阵运算可行解个数为f(n),我们可以先算前n-1个,再将n-1的结果和n相乘,也可以先算前n-2个,再算n-1和n,最后算前 A 1 A 2 . . . A n − 2 A_1A_2...A_{n−2} A1A2...An2 A n − 1 A n A_{n−1}A_n An1An,因此有如下递推关系:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n−1)+f(n−2) f(n)=f(n1)+f(n2)
一个斐波那契数列,它的时间复杂度是 O ( 2 n ) O(2^n) O(2n),指数级的。

解决方法

版本1

考虑分解成子问题,我们将 A 1 A 2 . . . A n A_1A_2...A_n A1A2...An分成两部分,先求左边的运算顺序,再求右边的运算顺序,最后将左右合并,但是我们无法直接知道最优的划分方案,所以将划分点设置为变量k,再对k进行遍历比较,找出最优的。

这里体现了DP和分治不同的地方,如果是分治就会选择中间点使得数据规模指数级下降

首先,设OPT(1,n)表示 A 1 A 2 . . . A n A_1A_2...A_n A1A2...An的最优运算次数,当确定分点k时,有以下递推式:
O P T ( 1 , n ) = O P T ( 1 , k ) + O P T ( k + 1 , n ) + p 0 p k p n OPT(1,n)=OPT(1,k)+OPT(k+1,n)+p_0p_kp_n OPT(1,n)=OPT(1,k)+OPT(k+1,n)+p0pkpn
其中 p 0 p k p n p_0p_kp_n p0pkpn是将(1,k)和(k+1,n)合并起来的运算开销。
然后,遍历k的位置,找出使得OPT(1,n)最小的。
推广到一般形式(i,j),得到整体递推公式如下:
O P T ( i , j ) = { 0 a m p ; if  i = = j ( 如 果 是 一 个 矩 阵 不 用 计 算 ) m i n k = 1 n − 1 { O P T ( 1 , k ) + O P T ( k + 1 , n ) + p 0 p k p n } a m p ; else  OPT(i,j) = \begin{cases} 0 &\text{if } i==j (如果是一个矩阵不用计算) \\ min_{k=1}^{n-1}\{OPT(1,k)+OPT(k+1,n) + p_0p_kp_n\}&\text{else } \end{cases} OPT(i,j)={ 0mink=1n1{ OPT(1,k)+OPT(k+1,n)+p0pkpn}amp;if i==j()amp;else 
伪代码,如下:
在这里插入图片描述
我们对版本1这个算法进一步分析可以发现,有很多冗余重复计算,下图红色的都是冗余重复计算。
在这里插入图片描述

版本2

因为重复计算造成耗时,我们可以用空间换时间,将计算结果保存下来,从而减少重复计算。可以发现上述树的计算次数和它的节点个数有关,我们用一个二维数组保存状态(i,j)的计算结果,每一次树要往下分支的时候,先查询以下数组,如果已经存在,则直接返回结果,避免产生新节点。伪代码如下:
在这里插入图片描述

版本3

用迭代替代递归
思考版本2的过程,递归从根节点先往下走,再往上走,当我想知道 A 1 . . . A n A_1...A_n A1...An时,我会向子节点询问,子节点如果没有直接结果,则再继续向孙子节点询问。在询问的过程中,为了避免重复计算,则用一个数组用于存储询问的中间结果。既然如此,我们完全可以从底层开始,先算完,子节点主动向自己的父节点汇报自己的计算结果,父节点再向祖先节点汇报,就可以避免自上而下的过程。算法的时间复杂度是 O ( n 3 ) O(n^3) O(n3)。把递归的伪代码转换为三重for循环(也就是用迭代替换递归):
在这里插入图片描述
举例运算
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结

1、最优化问题,首先考虑是否是多步决策

  • 解是否可以被逐步构建(与分治思想同源)
  • 目标函数是否可分(优化问题独有)

2、动态规划减少了许多冗余计算,所以快
3、例子的思路

  • 多步决策问题–>子问题定义–>递归表达式–>重复冗余子问题(例如特征表,空间换时间)

猜你喜欢

转载自blog.csdn.net/qq_32505207/article/details/107765811
今日推荐