3.9 流水作业调度问题
3.9 流水作业调度问题
博主简介:一个爱打游戏的计算机专业学生
博主主页: @夏驰和徐策
所属专栏:算法设计与分析
1.我对流水调度问题的理解
流水作业调度问题是动态规划中的一个经典问题,它涉及将一系列作业分配给多个工作站以最小化总完成时间。该问题的目标是确定作业的最优调度顺序,以使得所有作业的完成时间最小。
下面是该问题的一般描述和解决步骤:
1. 问题描述:
- 给定n个作业和m个工作站,每个作业有一个处理时间(ti)和一个工作站的要求(wj)。
- 每个工作站一次只能处理一个作业,且每个作业只能分配给一个工作站。
- 每个工作站在完成作业后会空闲,可以接受下一个作业。
2. 动态规划解决步骤:
- 定义状态:首先定义问题的状态。可以使用一个二维数组dp[i][j]表示前i个作业分配给j个工作站时的最小完成时间。
- 状态转移方程:根据最优子结构性质,可以推导出状态转移方程,即递推关系,来计算dp[i][j]。
- 边界条件:确定边界条件,如dp[0][j]和dp[i][0]的初始值。
- 递推计算:利用状态转移方程和边界条件,通过递推计算填充整个dp数组。
- 最优解的构造:根据dp数组中的最优值,逆向追溯得到最优解的调度顺序。
3. 时间复杂度:
- 动态规划算法的时间复杂度为O(nm),其中n是作业的数量,m是工作站的数量。
在流水作业调度问题中,关键是确定合适的状态定义和状态转移方程,以及正确处理边界条件。同时,根据实际情况,可能需要考虑其他约束条件,如工作站的容量限制或作业之间的关联关系等。因此,在解决流水作业调度问题时,需要综合考虑问题的特点和约束条件,设计合理的动态规划算法来求解最优调度方案。
1.最优子结构性质的证明:
要证明流水作业调度问题具有最优子结构性质,需要证明以下两个条件:
1. 最优子结构性质:如果一个问题的最优解包含了子问题的最优解,那么该问题具有最优子结构性质。
2. 子问题的最优解可以用来构造原问题的最优解。
下面是对流水作业调度问题的最优子结构性质的证明:
假设存在一个最优解J,它包含了作业序列J1, J2, ..., Jn的最优调度方案。我们将J分成两个部分,J1, J2, ..., Jk和Jk+1, Jk+2, ..., Jn,其中1 ≤ k ≤ n-1。
现在考虑子问题,即将J1, J2, ..., Jk和Jk+1, Jk+2, ..., Jn分别调度在两个不同的流水线上。假设存在另一个调度方案J',它的完成时间小于J的完成时间。
由于J'是一个最优解,那么J'中的子序列J1, J2, ..., Jk和Jk+1, Jk+2, ..., Jn的调度方案也必须是最优的。否则,我们可以通过替换J'中的这两个子序列的调度方案,得到一个更优的调度方案。
根据最优子结构的定义,我们可以使用子问题的最优解来构造原问题的最优解。假设我们知道了子问题J1, J2, ..., Jk和Jk+1, Jk+2, ..., Jn的最优调度方案。我们可以将两个子问题的调度方案合并,形成一个新的调度方案,即将J1, J2, ..., Jk的调度安排在第一个流水线上,将Jk+1, Jk+2, ..., Jn的调度安排在第二个流水线上。这样,我们就得到了原问题J的最优调度方案。
综上所述,流水作业调度问题具有最优子结构性质。这意味着我们可以通过解决子问题的最优解来构造原问题的最优解。这是使用动态规划等方法求解流水作业调度问题的关键性质。
2023/10/14重新证明:
要证明流水作业调度问题具有最优子结构性质,我们需要证明问题的最优解包含其子问题的最优解。
流水作业调度问题是要在两台机器上安排n个作业,以最小化完成所有作业的总时间。设我们已经得到了一个最优的调度π,即使得作业π(1), π(2), ..., π(n)是一个最优的顺序。
考虑去掉其中一个作业j,那么剩下的作业的顺序π(1), π(2), ..., π(j-1), π(j+1), ..., π(n)形成了一个子问题。
**我们需要证明以下两点**:
1. 这个子问题的解就是π(1), π(2), ..., π(j-1), π(j+1), ..., π(n)。
2. 如果π是最优解,则这个子问题的解也是最优的。
**证明**:
1. 假设子问题的一个最优解是π'(1), π'(2), ..., π'(n-1)且与π(1), π(2), ..., π(j-1), π(j+1), ..., π(n)不同。
2. 基于π'(1), π'(2), ..., π'(n-1)的调度顺序,我们可以为原始问题构造一个新的调度方式,即在适当的位置插入作业j,得到π''。
3. 如果π'(1), π'(2), ..., π'(n-1)比π(1), π(2), ..., π(j-1), π(j+1), ..., π(n)更好(完成时间更短),那么π''一定比π更好。
4. 但这与我们的假设相矛盾,即π是最优解。
5. 因此,我们得出结论,子问题的最优解确实是π(1), π(2), ..., π(j-1), π(j+1), ..., π(n)。
由以上论证,我们可以得出,流水作业调度问题的最优解包含其子问题的最优解,从而具有最优子结构性质。
3.流水作业调度的Johnson法则
我的理解:
我懒得画了累了。
图片来自B站Up:Up程振波
Johnson法则是一种常用于解决流水作业调度问题的启发式算法。它通过考虑作业的处理时间和机器之间的处理顺序,来确定作业的最佳调度顺序。
Johnson法则适用于两台机器的情况下,每个作业需要在这两台机器上按照一定顺序进行加工。下面是Johnson法则的基本步骤:
1. 首先,对于每个作业,将其处理时间分为两个部分:一个部分是在第一台机器上的处理时间,另一个部分是在第二台机器上的处理时间。
2. 接下来,计算每个作业在第一台机器上的处理时间和在第二台机器上的处理时间之和,并将作业按照这个总时间进行排序。
3. 根据排序结果,选择处理时间最短的作业,并将其安排在两台机器之间进行加工。
4. 不断重复上述步骤,选择处理时间最短的作业并进行安排,直到所有作业都被调度完毕。
Johnson法则的核心思想是通过优先处理处理时间较短的作业来减少整体的加工时间。它可以有效地降低作业的总处理时间,并提高生产效率。
需要注意的是,Johnson法则适用于只有两台机器的情况,并且要求作业的处理时间是可测量且可比较的。对于多台机器的情况,可以考虑其他调度算法来解决流水作业调度问题。
2023/10/14
另一种理解:(这种更贴合)
Johnson法则是处理两台机器流水线调度问题的一个经典算法。下面,我们将详细探讨其工作原理和实现方法:
**问题描述**:
你有`n`个作业和两台机器。每个作业首先在第一台机器上进行处理,然后在第二台机器上进行处理。作业`i`在第一台机器上的处理时间为`a[i]`,在第二台机器上的处理时间为`b[i]`。目标是确定作业的执行顺序,使得所有作业的完成时间最小。
**Johnson法则的工作原理**:
1. **分类**:
- 对于每个作业`i`,如果`a[i] < b[i]`,则作业`i`归入集合`N1`。
- 否则,作业`i`归入集合`N2`。
2. **排序**:
- 将`N1`中的作业按照`a[i]`的增序排序。
- 将`N2`中的作业按照`b[i]`的降序排序。
3. **组合**:
- 将`N1`和`N2`的排序结果组合在一起,得到最终的作业序列。
**为什么Johnson法则有效**:
这种分类和排序的方法背后有一个直观的解释。对于`N1`中的作业,因为它们在第一台机器上的处理时间较短,所以更早开始是有意义的。而对于`N2`中的作业,它们在第二台机器上的处理时间较长,所以将它们稍后开始是有意义的。
**实现过程**:
1. **初始化**: 创建一个结构数组`d`来存储每个作业的信息,包括它在两台机器上的处理时间和一个布尔值来确定它属于`N1`还是`N2`。
2. **分类**:
- 遍历所有作业。
- 如果`a[i] <= b[i]`,则`d[i].job = true`,并且`d[i].key = a[i]`。这意味着作业`i`属于`N1`。
- 否则,`d[i].key = b[i]`,这意味着作业`i`属于`N2`。
3. **排序**: 使用排序函数对`d`按`key`进行排序。
4. **重新排序**:
- 创建两个指针,一个从前往后(`j`),一个从后往前(`k`)。
- 遍历排序后的`d`数组,如果`d[i].job`为真,则该作业属于`N1`,并将其索引值存入数组`c`的前部。如果为假,则该作业属于`N2`,并将其索引值存入数组`c`的后部。
5. **计算总时间**:
- 使用`c`数组中的作业序列来计算所有作业的完成时间。
这样,你就得到了所有作业的最佳执行顺序,以及完成所有作业所需的总时间。
一、疑问将N2中的作业按照b[i]的降序排序。为什么要对N2进行降序排列
我的解答:
Johnson法则用于处理两台机器的流水线作业调度问题。其核心思想是对作业进行分类并排序,以最小化两台机器上的总完成时间。
对于一个特定的作业,如果它在第一台机器上的处理时间(称为ai
�
�
)小于或等于在第二台机器上的处理时间(称为bi
�
�
),那么这个作业被归为N₁;反之,如果它在第一台机器上的处理时间大于在第二台机器上的处理时间,则它被归为N₂。
现在,为什么要这样做?
1. 对于N₁中的作业,因为它们在第一台机器上的处理时间较短,所以我们希望它们尽可能早地开始,从而充分利用第一台机器。因此,我们按照ai
�
�
的升序排列N₁中的作业。
2. 对于N₂中的作业,它们在第二台机器上的处理时间比第一台机器上的要短。如果我们让它们早早完成在第一台机器上的作业,那么它们会在第二台机器上等待很长时间,因为它们在第二台机器上的处理时间很短。因此,我们希望它们在第一台机器上稍微晚一点开始,这样当它们完成第一台机器上的作业并转到第二台机器时,它们可以立即开始处理,不需要等待。为了实现这个目标,我们按照bi
�
�
的降序排列N₂中的作业。
通过这种方法,我们可以确保流水线上的每个作业在两台机器上的总体等待时间最短,从而实现最小的总完成时间。
4.算法描述
Johnson法则是一种用于解决流水作业调度问题的启发式算法,适用于具有两台机器的情况。该算法通过考虑作业的处理时间和机器之间的处理顺序,来确定作业的最佳调度顺序。
算法的基本描述如下:
1. 输入:一组作业列表,每个作业包含两个处理时间,分别表示在两台机器上的加工时间。
2. 初始化两个空的机器工作队列,分别表示第一台机器和第二台机器。
3. 对于每个作业,计算其在两台机器上的加工时间之和,并将作业按照这个总时间进行排序。
4. 选择处理时间最短的作业,并根据其在两台机器上的加工时间来确定加工顺序:
- 如果第一台机器上的加工时间小于等于第二台机器上的加工时间,则将该作业添加到第一台机器的工作队列中。
- 否则,将该作业添加到第二台机器的工作队列中。
5. 重复步骤4,直到所有作业都被调度完毕。
6. 输出最终的作业调度顺序。
Johnson法则的关键在于通过优先处理处理时间较短的作业来减少整体的加工时间。通过动态地选择作业的加工顺序,可以达到最小化作业的总处理时间的目标。
需要注意的是,Johnson法则只适用于两台机器的情况,并且要求作业的处理时间是可测量且可比较的。对于多台机器的情况,需要使用其他调度算法来解决流水作业调度问题。
5.算法复杂性分析
最坏情况,Flowshop所需的计算时间O(nlogn),所需空间显然位O(n)。
6.算法实现
### 一、信息
- 需要根据Johnson法则解决流水作业调度问题。
- 每项作业有两台机器的加工时间。
- 目标是找到最佳的作业序列,以最小化总完成时间。
### 二、分析
1. **信息中给出的条件的作用**:
- Johnson法则是解决此问题的已知方法。
- 加工时间是确定作业顺序的关键因素。
2. **思考过程**:
- **初始思路**:
1. 直观的想法是根据每项作业的加工时间的总和进行排序。但这种方法可能不会产生最优解。
2. 考虑单独按每台机器的加工时间排序。但这也不足以解决问题。
- **通过演绎推理**:
- Johnson法则提供了一个策略,即根据两台机器的加工时间将作业分为两类,并分别进行排序。通过推理,这种方法能确保总完成时间的最小化。
- **评价各自的优劣**:
- 直接按总时间排序或按单个机器时间排序的方法简单,但可能无法达到最优。
- Johnson法则虽然在思路上稍微复杂一些,但它确保了最优解。
- **选择实现方法**:
- 选择Johnson法则,因为它为此问题提供了已知的最优解。
3. **分析过程**:
- 对于每个作业,比较它在两台机器上的加工时间。
- 将作业分为两类:第一类是在第一台机器上加工时间较短的,第二类是在第二台机器上加工时间较短的。
- 对第一类按第一台机器的加工时间升序排序,对第二类按第二台机器的加工时间降序排序。
- 最后,将两类作业的排序结果组合起来,得到最优调度。
### 三、算法实现步骤
1. 定义一个作业类,存储两台机器的加工时间和作业的索引。
2. 对所有作业进行迭代,将它们分为两类。
3. 分别对这两类作业进行排序。
4. 将排序后的两类作业组合起来,得到最终的调度顺序。
### 四、实现过程中遇到的问题
1. **数据结构选择**:在开始时,需要决定如何存储每个作业的信息。结构体或类是一个明智的选择,因为它们可以存储作业的多个属性。
2. **排序策略**:实现Johnson法则需要对作业进行两次排序。需要确定在哪些条件下使用升序,以及在哪些条件下使用降序。
3. **代码的健壮性**:在实现过程中,要确保代码能够处理各种输入情况,例如作业数量为零的情况。
经过上述的思考和分析过程,我们可以得出一个明确的算法实现方案,并将其编码为一个工作的程序。
正确答案:
C语言:
#include <stdio.h>
#include <stdlib.h>
#define MAX_JOBS 100
typedef struct {
int processingTime[2];
int index;
} Job;
// Comparison function for Johnson's rule
int compareJobs(const void *a, const void *b) {
Job *jobA = (Job *)a;
Job *jobB = (Job *)b;
if (jobA->processingTime[0] <= jobA->processingTime[1]) {
if (jobB->processingTime[0] <= jobB->processingTime[1]) {
return jobA->processingTime[0] - jobB->processingTime[0];
}
return -1;
} else {
if (jobB->processingTime[0] <= jobB->processingTime[1]) {
return 1;
}
return jobB->processingTime[1] - jobA->processingTime[1];
}
}
void findOptimalSchedule(int n, Job jobs[], int schedule[]) {
qsort(jobs, n, sizeof(Job), compareJobs);
for (int i = 0; i < n; ++i) {
schedule[i] = jobs[i].index;
}
}
int main() {
int n;
Job jobs[MAX_JOBS];
int schedule[MAX_JOBS];
printf("Enter the number of jobs: ");
scanf("%d", &n);
printf("Enter the processing times:\n");
for (int i = 0; i < n; i++) {
scanf("%d %d", &jobs[i].processingTime[0], &jobs[i].processingTime[1]);
jobs[i].index = i + 1;
}
findOptimalSchedule(n, jobs, schedule);
printf("Optimal schedule: ");
for (int i = 0; i < n; i++) {
printf("%d ", schedule[i]);
}
printf("\n");
return 0;
}
运行结果:
C++:
#include <iostream>
#include <vector>
#include <algorithm>
struct Job {
int processingTime[2];
int index;
};
bool compareJobs(const Job& jobA, const Job& jobB) {
if (jobA.processingTime[0] <= jobA.processingTime[1]) {
if (jobB.processingTime[0] <= jobB.processingTime[1]) {
return jobA.processingTime[0] < jobB.processingTime[0];
}
return true;
}
else {
if (jobB.processingTime[0] <= jobB.processingTime[1]) {
return false;
}
return jobB.processingTime[1] < jobA.processingTime[1];
}
}
int main() {
int n;
std::cout << "Enter the number of jobs: ";
std::cin >> n;
std::vector<Job> jobs(n);
std::cout << "Enter the processing times:\n";
for (int i = 0; i < n; ++i) {
std::cin >> jobs[i].processingTime[0] >> jobs[i].processingTime[1];
jobs[i].index = i + 1;
}
std::sort(jobs.begin(), jobs.end(), compareJobs);
std::cout << "Optimal schedule: ";
for (const auto& job : jobs) {
std::cout << job.index << " ";
}
std::cout << std::endl;
return 0;
}
运行结果:
JAVA:
————————————————
版权声明:本文为CSDN博主「夏驰和徐策」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tang7mj/article/details/131029755