📜  O(n Log n)时间的加权作业调度(1)

📅  最后修改于: 2023-12-03 15:33:16.372000             🧑  作者: Mango

加权作业调度 - O(n Log n) 时间复杂度

在作业调度问题中,我们需要安排一些作业在有限的资源下完成,以最小化某种目标函数,如完成时间、延迟时间等。 常见的作业调度算法有贪心算法、动态规划等。其中,加权作业调度(Weighted Job Scheduling)问题是一道经典的贪心算法问题。

问题描述

考虑一个作业调度问题,有 $n$ 个作业,每个作业需要花费 $c_i$ 的时间来完成,每个作业有一个开始时间 $s_i$ 和结束时间 $f_i$。我们希望在不重叠的时间内安排所有作业,以最小化完成时间和为目标函数。即:

$$\text{minimize} \sum_{i=1}^{n}(w_i \cdot f_i)$$

其中,$w_i$ 是第 $i$ 个任务的重量。

算法思路

加权作业调度问题可以通过贪心算法解决,我们可按照如下步骤:

  1. 按照结束时间 $f_i$ 对任务进行排序
  2. 维护一个包含已安排的任务的集合 $S$
  3. 对于每个任务 $i$:
    1. 如果 $S$ 中没有任务与任务 $i$ 发生交集,则安排任务 $i$,将其加入到 $S$ 中
    2. 如果 $S$ 中已有任务与任务 $i$ 发生交集,则不安排任务 $i$
代码实现
class Job:
    def __init__(self, start, end, weight):
        self.start = start
        self.end = end
        self.weight = weight

def schedule(jobs):
    # 按结束时间排序
    jobs = sorted(jobs, key=lambda x: x.end)
    # 记录已安排的任务集合
    schedule_set = []
    for i, job in enumerate(jobs):
        # 查找是否有与任务 i 冲突的任务
        conflicting_job_idx = bisect_right(jobs, Job(0, job.start, 0), 0, i)
        conflicting_job = jobs[conflicting_job_idx - 1] if conflicting_job_idx > 0 else None
        if not conflicting_job:
            # 不存在冲突的任务,则安排任务 i
            schedule_set.append(job)
        else:
            # 存在冲突的任务,则根据贪心选择性地安排任务 i
            if sum(j.weight for j in schedule_set) < job.weight + sum(j.weight for j in jobs[:conflicting_job_idx]):
                schedule_set.remove(conflicting_job)
                schedule_set.append(job)
    return schedule_set
时间复杂度

算法的时间复杂度由排序的时间复杂度 $O(n\log n)$ 决定,因此总的时间复杂度为 $O(n\log n)$。

可视化实例

我们可以使用 Python 的 matplotlib 库对加权作业调度问题进行可视化。以下是一个样例图:

job_schedule

作业按照 ID 从左到右排列,每个作业的长度为它的持续时间。红色的线表示最优的排列方式,绿色的线表示我们贪心策略的排列方式。我们可以看到,贪心算法得到的结果已经很接近最优解。