任务调度问题(贪心算法) Java实现

这个问题是《算法导论》上的一个经典的贪心算法问题——单处理器上具有期限和惩罚的单位时间任务调度问题,目标是使惩罚最小。

问题描述:

具有截止时间和误时惩罚的单位时间任务时间表问题可描述如下:
(1) n 个单位时间任务的集合S={1,2,…,n}(n≤500);
(2) 任务i的截止时间deadline[i],1≤d[i]≤n,即要求任务i在时间d[i]之前结束;
(3) 任务i 的误时惩罚1≤weught[i]<1000,即任务i 未在时间d[i]之前结束将招致w[i]的惩罚;若按时完成则无惩罚。
任务时间表问题要求确定S 的一个时间表(最优时间表)使得总误时惩罚达到最小。
 

实验要求:

(1)实现这个问题的贪心算法
(2)将每个 wi 替换为max{m1,m2…mn}—wi,运行算法比较结果。

输入与输出:

输入为任务的截止时间和任务未按时完成的惩罚

int[] deadline = new int[]{4,2,4,3,1,4,6}; // 任务期限,范围是闭区间[1,n]
int[] weight = new int[]{70,60,50,40,30,20,10}; // 任务未按时完成的惩罚

输出为最小的惩罚值、任务的具体调度顺序。

总的惩罚为:50
任务执行顺序为:
第1天执行的任务为: Task{id=3, deadline=3, weight=40}
第2天执行的任务为: Task{id=1, deadline=2, weight=60}
第3天执行的任务为: Task{id=2, deadline=4, weight=50}
第4天执行的任务为: Task{id=0, deadline=4, weight=70}
第5天执行的任务为: 任选一个已超时任务执行
第6天执行的任务为: Task{id=6, deadline=6, weight=10}
第7天执行的任务为: 任选一个已超时任务执行

思路分析:

主要参考了这篇文章任务调度问题(贪心思想)_还能坚持的博客-CSDN博客_任务调度问题

贪心思路:贪心考虑的是局部的最优解,而不是全局的最优解,为了达到最小的误时惩罚,按照贪心策略,肯定是先去按时完成或提前完成惩罚较大的任务。若一个时间片上已经安排了按时完成的、有着较大惩罚的任务,那么,这个有着较小惩罚的任务,则需要提前完成(提前完成有着很多种方法,按照贪心策略,应该是放在紧邻的上一个时间片完成,若这个时间片也有任务,继续往前移,直到有空闲的时间片可以执行这个任务,否则,这个任务会带来惩罚)。


解题步骤1:根据罚时的大小进行排序,将惩罚大的放在前面。开一个数组route作为时间槽,记录每个单位时间是否有任务安排。尽量将任务安排在deadline当天完成,若当天已有任务则安排在前面最近的某一天。若在第deadline天及之前都有任务安排,先不要将该任务加入到时间槽中(可能产生后效性),可以将这个任务舍去,同时,将其增加到罚时中,等到对全部的任务过滤一遍后,可以将罚时的任务随意地插入到时间槽中 (既然都有罚时了,那么在截止时间后的任何一个时间片,完成该任务都一样)。

具体实现:

网上找了一圈,这道题基本上都是用cpp或python写的, 故来造一个java版本的轮子。

①任务结点定义:

public class Task implements Comparable<Task>{
    public int id;
    public int deadline;
    public int weight;

    public Task(int id, int deadline, int weight) {
        this.id = id;
        this.deadline = deadline;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Task{" +
                "id=" + id +
                ", deadline=" + deadline +
                ", weight=" + weight +
                '}';
    }

    @Override
    public int compareTo(Task newTask) { // 按惩罚由大到小排序,调用Arrays.sort时会用到
          return Integer.compare(newTask.weight, this.weight); 
    }

②贪心思想实现任务调度算法

import java.util.Arrays;


// 在单处理器上具有期限和惩罚的单位时间任务调度问题。
public class TaskDispatch {

    private static void greedyTaskDispatch(Task[] tasks) {
        int n = tasks.length;
        Arrays.sort(tasks); // 根据惩罚从大到小排序
        System.out.println("Task按惩罚从大到小排序后:");
        for(Task t : tasks) {
            System.out.println(t.toString());
        }

        int[] route = new int[n]; // 记录任务的最终调度顺序,route[i]表示第(i+1)天调度的任务id。天数从1开始,而数组下标从0开始。
        Arrays.fill(route, -1);

        int punishment = 0; // 记录总的惩罚值
        for(int i = 0; i < n; ++i) { // 处理第i个任务
            for(int j = tasks[i].deadline - 1; j >= 0; --j) { // deadline的范围是[1,n],故此处需修正下标
                // j为第i个任务的deadline,应将第i个任务尽可能在靠近deadline的天完成。
                // 这样卡点完成的好处是:让后续有更紧急deadline要求的任务更可能按时完成。
                if(route[j] == -1) { // 若第j天未安排任务
                    route[j] = tasks[i].id;
                    break;
                }
                if(j == 0) { // 第1天~第deadline天都已安排了任务,故当前任务必超时
                    punishment += tasks[i].weight;
                }
            }
        }

        System.out.println("总的惩罚为:" + punishment);
        System.out.println("任务执行顺序为:");
        for(int i = 0; i < n; ++i) {
            System.out.print("第" + (i+1) + "天执行的任务为:");
            if(route[i] != -1) {
                System.out.println(" " + tasks[route[i]]);
            }
            else {
                System.out.println(" 任选一个已超时任务执行");
            }
        }

    }

    public static void main(String[] args) {
        int n = 7; // 任务数量
        int[] deadline = new int[]{4,2,4,3,1,4,6}; // 任务期限,范围是闭区间[1,n]
        int[] weight = new int[]{70,60,50,40,30,20,10}; // 任务未按时完成的惩罚

        // 将 Wi 替换为 max{W1,W2……Wn} - Wi
//        int maxWeight = weight[0];
//        for(int w : weight) {
//            maxWeight = w > maxWeight ? w: maxWeight;
//        }
//        for(int i = 0; i < n; ++i) {
//            weight[i] = maxWeight - weight[i];
//        }

        Task[] tasks = new Task[n];
        for(int i = 0; i < n; ++i) {
            tasks[i] = new Task(i, deadline[i], weight[i]);
        }
        greedyTaskDispatch(tasks);
    }

}

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值