[算法笔记]动态规划DP、算法的描述

动态规划(Dynamic Programming)

相信大家在代码的实现的时候广泛地用到了递归,但是单纯的递归,虽然实现非常简便,但是我们知道在算法中,大部分时候无法两全,递归是函数对自身的调用,而这时需要时间与空间的,每一次的函数调用都会在内存栈中分配用来保存临时变量、保存参数和返回地址。并且,递归很多时候会有重复的计算步骤。最后,调用次数过多容易溢出。

动态规划极大地解决了重复调用的问题,与分治策略(Divide and Conquer)有显著区别的是,动态规划的子问题并不是毫无交集的,实际上动态规划和遍历算法很像,在设计的思路上并没有什么高超的地方,但是对于已经做过计算的保存,可以在重复调用时快速地计算出结果,节省时间。值得一提的是,所有能够使用贪心算法解决的问题都可以通过动态规划解决。很多具体算法背后都有动态规划的影子,如最短路径的弗洛伊德算法(Floyd-Warshall algorithm)

定义(Definition):

通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决的一种算法[1]

当然,这个定义不是最优的,我们将重复出现的子问题称为重叠子问题(overlapping sub-problem)。那么它的定义可以是:将一个问题拆分为若干个重叠子问题,之后将小问题的解结合得到大问题的解。

动态规划在计算科学(AI、graphics etc)、信息论、控制论等领域有广泛地应用

步骤(Step)

动态规划方法通常用来求解最优化问题。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值(最大值或最优值)的解。我们称这样的解为问题的最优解(an
optimal solution),而不是最优解(the optimal solution),因为可能有多个解都达到最优值[2. Thomas H.Cormen ,Charles E.Leiserson ,Ronald L.Rivest and Clifford Stein , 2019.6, China Machine Press, 204]

1.刻画一个最优解的结构特征
2.递归地定义最优解的值
3.计算最优解的值
4.利用计算出的信息构造一个最优解
前三步是动态规划求解问题的基础,如果我们仅仅需要是最优解的值而非解本身,那么可以忽略第四步

样例(Example)

一、斐波那契数列
观察下面这段代码

double F(int n){
	return (n<=1)?1:F(n-1)+F(n-2)
}

运行时间(run-time)为

Figure 1

计算得到T(n)=O(2^n)
如果我们需要计算F(44),需要计算F(43)F(42),而计算F(43),有需要计算F(42),当函数内的数变小时,情况更加不可控
在这里插入图片描述
Figure 2

我们当然不需要将*F(10)*计算差不多一千万次,所以需要解决这个问题
有这么一个可行的策略:
·为了避免重复计算,在表中存储中间计算的量(memorization)
·我们记忆下这些计算的值为了接下来可能重复的调用
在动态规划解决最优问题的过程中,自上而下的解决方案可能需要对于同一个子问题重复获取其最优解。这也是它和分治算法显著不同的地方,因为分治算法子问题完全不同,所以效果较好

#define MAX 100
int d[MAX]
int F(int n){
    
    
	if(n<=2)
	{
    
    
		return 1;
	}
	else
	{
    
    
		d[n-1]=F(n-1);
		d[n-2]=F(n-2);
		d[n]=d[n-1]+d[n-2];
		return d[n]
	}
}
//也可以用循环的方式
}

二、上课安排(interval scheduling)
·课程 j 在s[j]开始,在f[j]结束,并且重要程度为w[j]>0;
·当两个课程不重叠时,称这两个课程是可兼容的
目的(goal):找到一个拥有最大权重的互相兼容的课程集合
在这里插入图片描述
Figure 3

我们由浅入深,最暴力的方式(brute-force)当然是把各个课程的排列全部拿出来,看看哪个权重和最大啦。但是,这显然有很多改进的空间,比如说这些课程排列肯定有很多存在重合等等。那么怎么办呢?
当权重均为1时,可以用贪心算法(greedy algorithm),直接按照结束时间排序,从后面开始就行了。但是这里的加入了不同的权重,所以包含的课程数越多,并不一定代表最后得到的权重和最大
这时候DP就派上用场了,思想和上面的方法相似,但是需要加一个数组p来辅助

p[j]=小于j的最大的与课程j兼容的课程
在下图中p[2]=0,p[7]=3
在这里插入图片描述
Figure 4

我们将它更完整地呈现出来
Def. OPT(j)=任意互相兼容的拥有最大的权重和的课程子集,只含有课程1,2,…,j
Goal. OPT(n)=拥有最大权重和的互相兼容的课程集合
Case 1.OPT(j)不含有课程j
·所以它的解就是只含有课程1,2,…,j的最大权重和且互相兼容的课程集合

Case 2.OPT(j)含有课程j
·加入权重w[j]
·不含有不兼容的课程{p[j]+1,…,j-1}
·必须有包含1,2,…,p(j)最优解

经过上面的分析,是不是很清楚啦。动态规划的贝尔曼公式也出来了
在这里插入图片描述
Figure 5

下面分析一下算法复杂度(complexity)
注意到每次迭代时,最不济就是OPT(j-1),所以在动态规划过程中是至多跑2n次,是O(n),而p[j]的获取是对提供的课程区间按照结束时间进行sort以后,从后往前数得到的,总的复杂度是O(nlogn),加起来整个算法复杂度就是O(nlogn)

三、多点曲线拟合
再提一个我认为比较有趣的例子,我相信大家在做数据处理的时候都会用到曲线拟合,使我们研究过程中的一部分,但是当出现的点排布在不同区间变化很大时,就没办法用一条曲线进行拟合了,下面我们用动态规划来解决这个问题
首先看最基本的一个问题:

Least squares
给定平面内的n个点(x1,y1),(x2,y2),…,(xn,yn)。找一条线y=ax+b拟合
这种问题最常见的是使用最小二乘法
在这里插入图片描述
Figure 6
!](https://img-blog.csdnimg.cn/20210110134349420.png)

Figure 7

Segmented Least squares
·给定n个点(x1,y1),…,(xn,yn),x1<x2<…<xn,找到一组曲线使得f(x)的误差最小
在这里插入图片描述
Figure 8
f(x)=E+cL
E:各条直线的平方误差的和
L:直线条数
OPT(j)=对于点p1,…,pj的最小消耗
eij=pi,…,pj的SSE
那么每次的Cost=eij+c+OPT(i-1)
最后得到贝尔曼等式
在这里插入图片描述
Figure 9

样题(Sample)

所有的算法都需要下面这三个成分:核心思想(main idea)、正确性(proof of correctness)、复杂度分析(run time analysis)

1.算法的主要思想。 你应该在此部分正确传达算法的思想。 它不需要提供解决方案的所有详细信息或正确的原因。 例如,在动态编程问题中,你应该在这一部分中告诉我们子问题,基本情况和递归公式。你也可以用伪代码,这样看起来更加清晰

2.算法正确性的证明。不管输入是什么,你必须证明你的算法是正确的。对于循环和迭代算法,通常的方法是寻找不变量。一个循环不变量需要遵循以下三个原则:
1)在第一次迭代与循环之前它就是对的
2)如果在第i次迭代之前它是对的,那么在第i+1次之前它也必须是对的
3)如果在在最后迭代或循环之后它还是对的,那么你的输出就是对的
作为证明,这个不变量需要被系统仔细地描述,如果正确性在类(class)被证明正确,那么你就不用每次都证明它的正确性了

3.复杂度分析,通常是时间复杂度分析,用O(·)标记。并且证明为什么是这个复杂度

说了这么多给大家一个样例吧,这个样例来自于我的作业
给定一组长度为N的非负整数和数值W,判断这组数中是否存在子集,使得子集和等于W?
1.核心思想:令S表示这组输入的非负整数,我们建立一个布尔类型的二维数组dp[][]来实现动态规划,dp[i][j]表示是否存在包含S前i个元素的子集并且子集和为j,我们想要知道dp[N-1][W].我们先初始化,当j=0时,dp[i][j]=true,贝尔曼公式为
在这里插入图片描述
Figure 10

2.正确性
显然,由于空集是所有集合的子集,所以当j=0时,dp[i][j]=true。对于dp[i][j],如果第i个元素比j大,那么在最终答案里这个子集就不存在,dp[i][j]=dp[i-1][j]。否则存在两种情况,如果答案不包含第i个元素,那么dp[i][j]=dp[i-1][j]。如果答案包含第i个元素,那么子集和减小,dp[i][j]=dp[i-1][j-S[i]]。满足上面两种中任意一种情况d[i][j]都是对的

3.复杂度分析
因为有两层循环,其中一个是集合的长度,还有一个是值W,所以复杂度为θ(NW)

呜呜呜,翻译自己的作业好累,给大家看看我的答案吧
在这里插入图片描述
Figure 11

总结

总的来说,动态规划的思想非常简单,就是把之前算过的值存下来,从而省下重复计算的时间。分为三步,定义子问题,将主问题拆分为子问题,定下能够使小的子问题的答案构成大的子问题的答案的有小到大的子问题的顺序。分析的方法也非常朴素,如二选一、多选一、加入新的参数blablabla(写一篇自己还比较满意的博客差不多花掉我六个小时,要不是放假真的不敢想象)

猜你喜欢

转载自blog.csdn.net/Cplus_ruler/article/details/112414247
今日推荐