动态规划之装配线调度

2024-01-20 05:10
文章标签 动态 规划 调度 装配线

本文主要是介绍动态规划之装配线调度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:动态规划的概念

  动态规划(dynamic programming)是通过组合子问题的解而解决整个问题的。分治算法是指将问题划分为一些独立的子问题,递归的求解各个问题,然后合并子问题的解而得到原问题的解。例如归并排序,快速排序都是采用分治算法思想。本书在第二章介绍归并排序时,详细介绍了分治算法的操作步骤,详细的内容请参考:http://www.cnblogs.com/Anker/archive/2013/01/22/2871042.html。而动态规划与此不同,适用于子问题不是独立的情况,也就是说各个子问题包含有公共的子问题。如在这种情况下,用分治算法则会重复做不必要的工作。采用动态规划算法对每个子问题只求解一次,将其结果存放到一张表中,以供后面的子问题参考,从而避免每次遇到各个子问题时重新计算答案。

动态规划与分治法之间的区别:
(1)分治法是指将问题分成一些独立的子问题,递归的求解各子问题
(2)动态规划适用于这些子问题不是独立的情况,也就是各子问题包含公共子问题

  动态规划通常用于最优化问题(此类问题一般有很多可行解,我们希望从这些解中找出一个具有最优(最大或最小)值的解)。动态规划算法的设计分为以下四个步骤:

(1)描述最优解的结构

(2)递归定义最优解的值

(3)按自低向上的方式计算最优解的值

(4)由计算出的结果构造一个最优解

  动态规划最重要的就是要找出最优解的子结构。书中接下来列举4个问题,讲解如何利用动态规划方法来解决。动态规划的内容比较多,我计划每个问题都认真分析,写成日志。今天先来看第一个问题:装配线调度问题

2、问题描述

  一个汽车公司在有2条装配线的工厂内生产汽车,每条装配线有n个装配站,不同装配线上对应的装配站执行的功能相同,但是每个站执行的时间是不同的。在装配汽车时,为了提高速度,可以在这两天装配线上的装配站中做出选择,即可以将部分完成的汽车在任何装配站上从一条装配线移到另一条装配线上。装配过程如下图所示:

  装配过程的时间包括:进入装配线时间e、每装配线上各个装配站执行时间a、从一条装配线移到另外一条装配线的时间t、离开最后一个装配站时间x。举个例子来说明,现在有2条装配线,每条装配线上有6个装配站,各个时间如下图所示:

从图中可以看出按照红色箭头方向进行装配汽车最快,时间为38。分别现在装配线1上的装配站1、3和6,装配线2上装配站2、4和5。

3、动态规划解决步骤

(1)描述通过工厂最快线路的结构

  对于装配线调度问题,一个问题的(找出通过装配站Si,j 最快线路)最优解包含了子问题(找出通过S1,j-1或S2,j-1的最快线路)的一个最优解,这就是最优子结构。观察一条通过装配站S1,j的最快线路,会发现它必定是经过装配线1或2上装配站j-1。因此通过装配站的最快线路只能以下二者之一:

a)通过装配线S1,j-1的最快线路,然后直接通过装配站Si,j

b)通过装配站S2,j-1的最快线路,从装配线2移动到装配线1,然后通过装配线S1,j

为了解决这个问题,即寻找通过一条装配线上的装配站j的最快线路,需要解决其子问题,即寻找通过两条装配线上的装配站j-1的最快线路。

(2)一个递归的解

  最终目标是确定底盘通过工厂的所有路线的最快时间,设为f*,令fi[j]表示一个底盘从起点到装配站Si,j的最快时间,则f* = min(f1[n]+x1,f2[n]+x2)。逐步向下推导,直到j=1。

当j=1时: f1[1] = e1+a1,1,f2[1] = e2+a2,1

当j>1时:f1[j] = min(f1[j-1]+a1,j,f2[j-1]+t2,j-1+a1,j),f2[j] = min(f2[j-1]+a2,j,f1[j-1]+t1,j-1+a2,j)

(3)计算最快时间

  有了递归的解,就可以按照上述的思路编写程序实现,为了避免用递归实现,需要开辟辅助空间来进行,以空间来换取时间,用C语言实现如下所示:

复制代码
 1 void fastest_way(int a[][N],int t[][N-1],int e[],int x[],int f[][N],int l[][N],int n)
 2 {
 3     int i,j;
 4     f[0][0] = e[0] + a[0][0];
 5     f[1][0] = e[1] + a[1][0];
 6     l[0][0] = 1;
 7     l[1][0] = 2;
 8     for(j=1;j<n;j++)
 9     {
10         if(f[0][j-1] < f[1][j-1] + t[1][j-1])
11         {
12             f[0][j] = f[0][j-1] + a[0][j];
13             l[0][j] = 1;
14         }
15         else
16         {
17             f[0][j] = f[1][j-1] + t[1][j-1] + a[0][j];
18             l[0][j] = 2;
19         }
20         if(f[1][j-1] < f[0][j-1] + t[0][j-1])
21         {
22             f[1][j] = f[1][j-1] + a[1][j];
23             l[1][j] = 2;
24         }
25         else
26         {
27             f[1][j] = f[0][j-1] + t[0][j-1] + a[1][j];
28             l[1][j] = 1;
29         }
30     }
31     if(f[0][n-1] + x[0] < f[1][n-1] + x[1])
32     {
33         last_f = f[0][n-1] + x[0];
34         last_l = 1;
35     }
36     else
37     {
38         last_f = f[1][n-1] + x[1];
39         last_l = 2;
40     }
41 }
复制代码

(4)构造通过工厂的最快线路

  有第三步骤已经计算出来并记录了每个装配站所在的装配线编号,故可以按照以站号递减顺序直接输出,程序如下所示:

复制代码
 1 void print_station(int l[][N],int last_l,int n)
 2 {
 3     int i = last_l;
 4     int j;
 5     printf("line %d,station %d\n",i,n);
 6     for(j=n-1;j>0;--j)
 7     {
 8         i = l[i-1][j];
 9         printf("line %d,station %d\n",i,j);
10     }
11 }
复制代码

  若是按照站号递增顺序输出,则需通过递归进行实现,程序如下所示:

复制代码
 1 void print_station_recursive(int l[][N],int last_l,int n)
 2 {
 3     int i = last_l;
 4     if(n == 1)
 5         printf("line %d,station %d\n",i,n);
 6     else
 7     {
 8          print_station_recursive(l,l[i-1][n-1],n-1);
 9          printf("line %d,station %d\n",i,n);
10     }
11 
12 }
复制代码

4、编程实现

根据上面的分析,采用C语言实现如下:

复制代码
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define N 6
  5 
  6 void fastest_way(int a[][N],int t[][N-1],int e[],int x[],int f[][N],int l[][N],int n);
  7 void print_station(int l[][N],int last_l,int n);
  8 void print_station_recursive();
  9 //全局变量,last_t表示最短时间,last_l表示最后一个装配站所在的装配线编号
 10 int last_f,last_l;
 11 
 12 int main()
 13 {
 14     int a[2][6] = {{7,9,3,4,8,4},{8,5,6,4,5,7}};
 15     int t[2][5] = {{2,3,1,3,4},{2,1,2,2,1}};
 16     int f[2][6] = {0};
 17     int l[2][6] = {0};
 18     int e[2] = {2,4};
 19     int x[2] = {3,2};
 20     int i,j;
 21     fastest_way(a,t,e,x,f,l,6);
 22     //打印输出各个装配线上各个装配站执行的最短时间
 23     for(i=0;i<2;++i)
 24     {
 25         printf("f%d is: ",i+1);
 26         for(j=0;j<6;++j)
 27           printf("%d ",f[i][j]);
 28         printf("\n");
 29     }
 30     printf("last_f is: %d\nlast_l is: %d\n",last_f,last_l);
 31     for(i=0;i<2;++i)
 32     {
 33         printf("l%d is: ",i+1);
 34         for(j=0;j<6;++j)
 35           printf("%d ",l[i][j]);
 36         printf("\n");
 37     }
 38     print_station(l,last_l,6);
 39     printf("output sequence by recursive.\n");
 40     print_station_recursive(l,last_l,6);
 41     return 0;
 42 }
 43 
 44 void fastest_way(int a[][N],int t[][N-1],int e[],int x[],int f[][N],int l[][N],int n)
 45 {
 46     int i,j;
 47     f[0][0] = e[0] + a[0][0];
 48     f[1][0] = e[1] + a[1][0];
 49     l[0][0] = 1;
 50     l[1][0] = 2;
 51     for(j=1;j<n;j++)
 52     {
 53         if(f[0][j-1] < f[1][j-1] + t[1][j-1])
 54         {
 55             f[0][j] = f[0][j-1] + a[0][j];
 56             l[0][j] = 1;
 57         }
 58         else
 59         {
 60             f[0][j] = f[1][j-1] + t[1][j-1] + a[0][j];
 61             l[0][j] = 2;
 62         }
 63         if(f[1][j-1] < f[0][j-1] + t[0][j-1])
 64         {
 65             f[1][j] = f[1][j-1] + a[1][j];
 66             l[1][j] = 2;
 67         }
 68         else
 69         {
 70             f[1][j] = f[0][j-1] + t[0][j-1] + a[1][j];
 71             l[1][j] = 1;
 72         }
 73     }
 74     if(f[0][n-1] + x[0] < f[1][n-1] + x[1])
 75     {
 76         last_f = f[0][n-1] + x[0];
 77         last_l = 1;
 78     }
 79     else
 80     {
 81         last_f = f[1][n-1] + x[1];
 82         last_l = 2;
 83     }
 84 }
 85 
 86 void print_station(int l[][N],int last_l,int n)
 87 {
 88     int i = last_l;
 89     int j;
 90     printf("line %d,station %d\n",i,n);
 91     for(j=n-1;j>0;--j)
 92     {
 93         i = l[i-1][j];
 94         printf("line %d,station %d\n",i,j);
 95     }
 96 }
 97 void print_station_recursive(int l[][N],int last_l,int n)
 98 {
 99     int i = last_l;
100     if(n == 1)
101         printf("line %d,station %d\n",i,n);
102     else
103     {
104          print_station_recursive(l,l[i-1][n-1],n-1);
105          printf("line %d,station %d\n",i,n);
106     }
107 
108 }
复制代码

程序执行结果如下所示:

5、总结

  动态规划是个非常有效的设计方法,要善于用动态规划去分析问题,重点是如何发现子问题的结构。最优子结构在问题域中以两种方式变化(在找出这两个问题的解之后,构造出原问题的最优子结构往往就不是难事了):

a) 有多少个子问题被用在原问题的一个最优解中
b) 在决定一个最优解中使用哪些子问题有多少个选择

 

这篇关于动态规划之装配线调度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/624884

相关文章

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

.NET利用C#字节流动态操作Excel文件

《.NET利用C#字节流动态操作Excel文件》在.NET开发中,通过字节流动态操作Excel文件提供了一种高效且灵活的方式处理数据,本文将演示如何在.NET平台使用C#通过字节流创建,读取,编辑及保... 目录用C#创建并保存Excel工作簿为字节流用C#通过字节流直接读取Excel文件数据用C#通过字节