动态规划之 装配线调度问题

从之前提到的最长公共子序列的问题中已经可以看到动态规划的应用之处,但是对于这种算法,或者说是一种思想,该在什么地方使用,哪些问题的解决可以使用动态规划,可能并不清晰。下文所讲述的内容就是可用动态规划解决问题的两个要素:最优子结构和重叠子问题。

在分析这两个要素之前,先以两个例子引入:

装配线调度

假设一个汽车底盘加工有两个装配线,如下如所示,每个装配线都有n个配件站,用于给底盘安装不同的零件,配件站用S表示,如S(2,3)表示第二条装配线的第三个配件站。两条装配线同一个配件站的工作相同,但是时间不同,用a表示所花费的时间,如a(2,3)表示装配站2的第三个配件站完成工作的时间。底盘进入装配线的时间用e表示,离开装配线的时间用x表示。

《动态规划之 装配线调度问题》

一般情况下,一个汽车底盘的装配过程就是进入一条装配线然后依次通过配件展,一条装配线内从一个配件站到另一个的时间可以忽略。但是某些时候可能会需要一些加急的订单,对这些订单仍旧需要经过所有的配件站,但不能在一条装配线上一直运行,这样的最短时间就无法保证。解决方法就是从一条装配线的配件站转移到另一条装配线的配件站,但转移的过程需要时间,记为t如上图,问题就是要寻找需要在哪些配件站进行转移达到时间的最小化。在下图中所示例子可以得到一种最优转移方案:

《动态规划之 装配线调度问题》

或许蛮力求解是一种解决方案,但当配件站的数目一旦增多,时间复杂度是按照指数的形式增长的,所以和最长公共子序列一样,穷举是不可能的。按照LCS问题一样,给出4个步骤:

步骤一:通过工厂最快路线的结构

第一步其实就是要描述最优解结构的特征,假设底盘到达配件站S(1,j)的路径是最短的,那么到达这个配件站的前一个配件站可以是S(1,j-1)或者是S(2,j-1),当然,到达S(1,j-1)或者S(2,j-1)的路径同样是最短的。到达S(1,j)的最优解包含了到达S(1,j-1)或者是S(2,j-1)的最优解,我们称这个性质为最优子结构,这也是能否使用动态规划的一个标志。

下面就是利用最优子结构来进行说明,用子问题的一个最优解来构造原问题的最优解。通过配件站S(1,j)的最快路线,必然装配线1或者2的j-1的配件站,因此,通过S(1,j)的最快路线只能是以下两种选择:

·通过配件站S(1,j-1),然后直接到达S(1,j)。

·通过配件站S(2,j-1),然后从装配线2到装配线1,最后到达S(1,j)。

当然,对于配件站S(2,j)的路径是对称的。所以,要解决到达某一个配件站的最短时间,就要解决两个字问题的最短时间问题,而通过对子问题的求解就可以构造出问题的最优解。

步骤二:一个递归的解

在动态规划的第二步中,就是通过对子问题的最优解来递归定义一个最优解,在装配线问题中,我们就选择达到配件站j的最快路线的问题作为子问题,令fi[j]表示底盘从起点到达配件站S(i,j)的最快时间,而我们的目的就是确定底盘到达最后出口的最短时间,记为f*,每条装配线的配件站的数目都是n,于是有:

《动态规划之 装配线调度问题》

对于f(1,1)和f(2,1)的推导也是很容易:

《动态规划之 装配线调度问题》

现在的问题就是该如何计算f(i,j),而f(i,j)就是之前所述的直接通过同一装配线的上一个配件站或者通过不同装配线然后到达S(i,j),于是就有:

《动态规划之 装配线调度问题》

f(i,j)的值就是子问题最优解的值,为了有助于跟踪最优解的构造过程,定义l(i,j)为装配线的编号(1或2),其中的配件站j-1被通过配件站S(i,j)的的最快路径所使用。举个例子吧,就例如上图,l(1,6)的值就是2,表示向后应该找装配线2上的配件站了,对应于S(2,5),而l(2,5)=2,那么下一个对应于配件站S(2,4)。

步骤三:计算最快时间

通过前面得到的递归可以很快算出来,下面给出伪代码:

《动态规划之 装配线调度问题》   

《动态规划之 装配线调度问题》

在这段代码中,显示直接可以计算出f(1,1)和f(2,1)的值,然后通过递归式子算出每一个f(1,j)和f(2,j)的大小,用l变量保存每个f(i,j)之前的那个配件站是从哪条装配线过来的,最后通过x变量求出最短的时间f*。

步骤四:构造通过工厂的最快路线

由于变量l已经记录好了之前的那一个配件站的位置,所以只要一路回溯过去就行了

《动态规划之 装配线调度问题》

程序的源代码如下:

#include <stdio.h> #include <stdlib.h> /* 下面的公共数据是对配件站的模拟,f_star对应于文章中的f*,l_star对应于l* 其余路径均一数组形式存放,所有的数据都是从1开始计算,所以数组第0号位置全部存放0 */ int f_star,l_star; int f[2+1][6+1]; int l[2+1][6+1]; int a[2+1][6+1]={{0,0,0,0,0,0,0},{0,7,9,3,4,8,4},{0,8,5,6,4,5,7}}; int t[2+1][5+1]={{0,0,0,0,0,0},{0,2,3,1,3,4},{0,2,1,2,2,1}}; int e[2+1]={0,2,4}; int x[2+1]={0,3,2}; void FASTEST_WAY() { int j; f[1][1]=e[1]+a[1][1];f[2][1]=e[2]+a[2][1]; for(j=2;j<=6;j++) { if(f[1][j-1]+a[1][j]<=f[2][j-1]+t[2][j-1]+a[1][j]) { f[1][j]=f[1][j-1]+a[1][j]; l[1][j]=1; } else { f[1][j]=f[2][j-1]+t[2][j-1]+a[1][j]; l[1][j]=2; } if(f[2][j-1]+a[2][j]<=f[1][j-1]+t[1][j-1]+a[2][j]) { f[2][j]=f[2][j-1]+a[2][j]; l[2][j]=2; } else { f[2][j]=f[1][j-1]+t[1][j-1]+a[2][j]; l[2][j]=1; } } if(f[1][6]+x[1]<=f[2][6]+x[2]) { f_star=f[1][6]+x[1]; l_star=1; } else { f_star=f[2][6]+x[2]; l_star=2; } } void PRINT_STATIONS() { int j; int i=l_star; printf("line %d,station %d\n",i,6); for(j=6;j>=2;j--) { i=l[i][j]; printf("line %d,station %d\n",i,j-1); } } int main() { FASTEST_WAY(); printf("最短路线f_star=%d\n",f_star); printf("以下是每个配件站的位置:\n"); PRINT_STATIONS(); return 0; } 

    原文作者:动态规划
    原文地址: https://blog.csdn.net/yettocome/article/details/9832361
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞