假设我们有n
个作业要进行调度,我们想找到一个作业调度顺序以使总损失最小。每个作业具有三个属性:(b,l,k)
,其中b
指示每个作业的即将到来时间,l
指示其运行所需的时间,而k
是系数。提供工作后,不能抢占,而我们只有一台机器。作业的等待时间定义为服务时间-即将到来的时间。我们将作业的丢失定义为等待时间乘以k。
例如,我们有三个工作:(0,10,1), (2,3,5), (4,7,3)
。如果我们遵循其初始顺序1,2,3
,则每个工作的损失分别为0, (10-2)×5=40, (13-4)×3=27
,总损失为67。但是,如果我们遵循顺序2,3,1
,则可以得到较低的总损失0+12+3=15
。这意味着,尽管第一个作业在时间0到来,但我们不处理它。相反,在完成第三项工作之前,我们选择第一项工作进行计划。这是按照顺序2,3,1
我认为这个问题与操作系统中的SJF算法有一些相似之处,但彼此之间也有很大不同。
这是我思考的主要部分。基本上,我想出了贪婪算法。我维持deque
来保留工作,但是没有及时处理。当我们需要选择一个作业进行调度时,我考虑了双端队列中的作业以及正在提供作业时的作业。我比较这些工作之间的损失,以找到增加损失的工作。好吧...我的英语不好。如果有人想了解更多信息,请转到我的github repo
我想知道是否存在动态规划算法或其他启发式算法以获得更好的近似解决方案?
下面是我的代码的主要部分,用C++
编写:
vector<int> order;
deque<seg> q;
int curr_idx = 0, curr_time = 0;
long long total_loss = 0;
while (curr_idx < n || !q.empty()) {
//当所有任务都已入队,直接处理队中任务
if (curr_idx == n) {
//计算队中所有k之和
int k_sum = 0;
for (auto &s : q) {
k_sum += s.k;
}
while (!q.empty()) {
//比较选择哪个任务可以带来最小的*新增*损失
int min_loss = INT_MAX;
deque<seg>::iterator min_it = q.begin();//换成auto也行
for (auto it = q.begin();it != q.end();it++) {
int loss = it->last*(k_sum - it->k);
if (loss < min_loss) {
min_it = it;
min_loss = loss;
}
}
order.push_back(min_it->beg);
total_loss += min_loss;
curr_time += min_it->last;
k_sum -= min_it->k;
q.erase(min_it);
}
break;
}
//系统初始情况或者期间没有任务及时到来
//推进当前时间,相当于指针在时间轴往前跑,直到找到一个新的任务
//while用来将同时刻的任务全部入队
if (q.empty()) {
int time = segs[curr_idx].beg;
while (curr_idx < n&&segs[curr_idx].beg == time)
q.push_back(segs[curr_idx++]);
curr_time = q.front().beg;
}
int k_sum = 0;
for (auto &s : q) {
k_sum += s.k;
}
//比较选择哪个队中任务可以带来最小的*新增*损失
int min_loss = INT_MAX;
deque<seg>::iterator min_it = q.begin();//换成auto也行
int lower_idx = -1;
bool in_queue = true;
for (auto it = q.begin();it != q.end();it++) {
int loss = it->last*(k_sum - it->k);//若选当前任务,当其执行完毕时的队列其他任务新增损失
int idx = curr_idx;
//区间内到达任务由于目前在处理it任务而造成的新增损失
while (idx < n&&segs[idx].beg <= curr_time + it->last) {
loss += (curr_time + it->last - segs[idx].beg)*segs[idx].k;
idx++;
}
if (loss < min_loss) {
min_loss = loss;
min_it = it;
in_queue = true;
}
//开始比较是否可以先执行在后续区间内的任务能带来更小的损失
for (int i = curr_idx;i < n&&segs[i].beg <= curr_time + it->last;i++) {
//队列中所有任务由于继续等待造成的新增损失
int end_time = segs[i].beg + segs[i].last;
int loss2 = (end_time - curr_time)*k_sum;
for (int j = curr_idx;j < n&&segs[j].beg <= end_time;j++) {
if (j != i)
loss2 += (end_time - segs[j].beg)*segs[j].k;
}
if (loss2 < min_loss) {
min_loss = loss2;
in_queue = false;
lower_idx = i;
}
}
}
total_loss += min_loss;
//区间内没有可以造成更小损失的任务,执行队内任务
if (in_queue) {
curr_time += min_it->last;
order.push_back(min_it->beg);
q.erase(min_it);
while (curr_idx < n&&segs[curr_idx].beg <= curr_time) {
q.push_back(segs[curr_idx]);
curr_idx++;
}
}
else {
order.push_back(segs[lower_idx].beg);
curr_time = segs[lower_idx].beg + segs[lower_idx].last;
//将该任务前的到达任务入队,并注意跳过该任务
while (curr_idx < n&&segs[curr_idx].beg <= curr_time) {
if (curr_idx != lower_idx)
q.push_back(segs[curr_idx]);
curr_idx++;
}
}
}