当前位置: 首页 > news >正文

贪心算法应用:最短作业优先(SJF)调度问题详解

在这里插入图片描述

Java中的贪心算法应用:最短作业优先(SJF)调度问题详解

一、贪心算法基础概念

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。

贪心算法的特点:

  1. 局部最优选择:在每一步都做出当前看起来最佳的选择
  2. 不可回退:一旦做出选择,就不会再改变
  3. 高效性:通常比其他全局最优算法更高效
  4. 不保证全局最优:在某些问题中可能无法得到全局最优解

贪心算法的适用条件:

  1. 贪心选择性质:局部最优选择能导致全局最优解
  2. 最优子结构:问题的最优解包含其子问题的最优解

二、最短作业优先(SJF)问题概述

最短作业优先(Shortest Job First, SJF)是一种CPU调度算法,它选择下一个具有最短执行时间的进程/作业来执行。SJF可以是:

  • 非抢占式:一旦CPU分配给进程,进程会一直运行直到完成
  • 抢占式:也称为最短剩余时间优先(SRTF),新进程到达时,如果其执行时间比当前进程剩余时间短,则抢占CPU

SJF算法的优点:

  1. 最小化平均等待时间
  2. 最大化系统吞吐量

SJF算法的缺点:

  1. 可能导致长作业饥饿
  2. 需要预先知道或估计作业的执行时间
  3. 实现比FCFS(先来先服务)更复杂

三、SJF问题的贪心算法分析

在SJF调度中,贪心算法的体现是:每次总是选择剩余作业中最短的一个来执行。这种局部最优选择(每次选最短作业)能够导致全局最优(最小化平均等待时间)。

数学证明:

设有n个作业,执行时间分别为t₁, t₂, …, tₙ,按执行时间排序为t₁ ≤ t₂ ≤ … ≤ tₙ。

作业i的等待时间Wᵢ = Σ(tⱼ) for j=1 to i-1

总等待时间 = ΣWᵢ = Σ(Σtⱼ) for j=1 to i-1, i=1 to n

要最小化总等待时间,应该让短作业先执行,因为短作业在前会减少后面所有作业的等待时间。

四、非抢占式SJF的Java实现

1. 作业/进程类定义

class Process {int pid;        // 进程IDint arrivalTime; // 到达时间int burstTime;   // 执行时间public Process(int pid, int arrivalTime, int burstTime) {this.pid = pid;this.arrivalTime = arrivalTime;this.burstTime = burstTime;}
}

2. 非抢占式SJF算法实现

import java.util.*;public class NonPreemptiveSJF {// 计算等待时间和周转时间static void findAvgTime(Process[] processes) {int n = processes.length;int[] waitingTime = new int[n];int[] turnaroundTime = new int[n];// 按到达时间排序Arrays.sort(processes, Comparator.comparingInt(p -> p.arrivalTime));// 创建优先队列,按执行时间排序PriorityQueue<Process> pq = new PriorityQueue<>(Comparator.comparingInt(p -> p.burstTime));int currentTime = 0;int index = 0;int completed = 0;while (completed != n) {// 将所有到达时间<=currentTime的进程加入队列while (index < n && processes[index].arrivalTime <= currentTime) {pq.add(processes[index]);index++;}if (pq.isEmpty()) {currentTime = processes[index].arrivalTime;continue;}// 获取执行时间最短的进程Process current = pq.poll();// 计算等待时间waitingTime[current.pid] = currentTime - current.arrivalTime;// 计算周转时间turnaroundTime[current.pid] = waitingTime[current.pid] + current.burstTime;// 更新当前时间currentTime += current.burstTime;completed++;}// 计算平均等待时间和周转时间float totalWaitingTime = 0, totalTurnaroundTime = 0;for (int i = 0; i < n; i++) {totalWaitingTime += waitingTime[i];totalTurnaroundTime += turnaroundTime[i];}System.out.println("平均等待时间 = " + (totalWaitingTime / n));System.out.println("平均周转时间 = " + (totalTurnaroundTime / n));}public static void main(String[] args) {Process[] processes = {new Process(0, 0, 6),new Process(1, 2, 8),new Process(2, 4, 7),new Process(3, 5, 3)};findAvgTime(processes);}
}

3. 代码解析

  1. 排序阶段:首先按到达时间排序所有进程
  2. 优先队列:使用最小堆(优先队列)来快速获取当前可执行的最短作业
  3. 时间推进
    • 将已到达的进程加入队列
    • 如果队列为空,时间跳到下一个进程到达时间
    • 取出最短作业执行
    • 计算等待时间和周转时间
  4. 统计结果:计算并输出平均等待时间和周转时间

五、抢占式SJF(SRTF)的Java实现

抢占式SJF,即最短剩余时间优先(Shortest Remaining Time First, SRTF),实现更为复杂。

1. 抢占式SJF算法实现

import java.util.*;public class PreemptiveSJF {static class Process {int pid;int arrivalTime;int burstTime;int remainingTime;int completionTime;int waitingTime;int turnaroundTime;public Process(int pid, int arrivalTime, int burstTime) {this.pid = pid;this.arrivalTime = arrivalTime;this.burstTime = burstTime;this.remainingTime = burstTime;}}static void findAvgTime(Process[] processes) {int n = processes.length;int currentTime = 0;int completed = 0;// 按到达时间排序Arrays.sort(processes, Comparator.comparingInt(p -> p.arrivalTime));// 优先队列,按剩余时间排序PriorityQueue<Process> pq = new PriorityQueue<>(Comparator.comparingInt(p -> p.remainingTime));int index = 0;Process current = null;while (completed != n) {// 将到达的进程加入队列while (index < n && processes[index].arrivalTime <= currentTime) {pq.add(processes[index]);index++;}// 如果有更高优先级的进程(更短剩余时间)if (current != null && !pq.isEmpty() && pq.peek().remainingTime < current.remainingTime) {pq.add(current);current = pq.poll();}// 如果没有正在执行的进程,从队列取一个if (current == null && !pq.isEmpty()) {current = pq.poll();}// 时间推进currentTime++;if (current != null) {current.remainingTime--;// 如果进程完成if (current.remainingTime == 0) {current.completionTime = currentTime;current.turnaroundTime = current.completionTime - current.arrivalTime;current.waitingTime = current.turnaroundTime - current.burstTime;completed++;current = null;}}}// 计算平均时间float totalWaitingTime = 0, totalTurnaroundTime = 0;for (Process p : processes) {totalWaitingTime += p.waitingTime;totalTurnaroundTime += p.turnaroundTime;}System.out.println("平均等待时间 = " + (totalWaitingTime / n));System.out.println("平均周转时间 = " + (totalTurnaroundTime / n));}public static void main(String[] args) {Process[] processes = {new Process(0, 0, 8),new Process(1, 1, 4),new Process(2, 2, 9),new Process(3, 3, 5)};findAvgTime(processes);}
}

2. 代码解析

  1. 进程类扩展:增加了剩余时间、完成时间等字段
  2. 时间推进:以单位时间(1)推进
  3. 抢占逻辑
    • 每次时间推进前检查是否有更高优先级(更短剩余时间)的进程
    • 如果有,当前进程放回队列,执行新进程
  4. 进程完成:当剩余时间为0时,计算各项时间指标
  5. 统计结果:计算并输出平均等待时间和周转时间

六、SJF算法的性能分析

时间复杂度分析

  1. 非抢占式SJF

    • 排序:O(n log n)
    • 优先队列操作:每次插入和删除O(log n),共n次 → O(n log n)
    • 总时间复杂度:O(n log n)
  2. 抢占式SJF(SRTF)

    • 排序:O(n log n)
    • 每个时间单位可能有一次队列操作:最坏情况O(T log n),T为总执行时间
    • 实际应用中,T可能很大,因此SRTF开销较大

空间复杂度分析

两种实现都使用了优先队列,最坏情况下需要存储所有进程:

  • 空间复杂度:O(n)

七、SJF算法的变种与优化

1. 预测执行时间

实际系统中作业的执行时间往往未知,可采用以下方法预测:

  • 指数平均法:τₙ₊₁ = αtₙ + (1-α)τₙ
    • τₙ₊₁:下一次预测值
    • tₙ:实际执行时间
    • τₙ:上一次预测值
    • α:权重参数(0≤α≤1)

2. 多级反馈队列(MFQ)

结合SJF和轮转调度(RR)的优点:

  1. 新作业进入最高优先级队列(短作业优先)
  2. 如果作业运行时间过长,降级到下一队列
  3. 较低优先级队列采用更长时间片的RR
  4. 防止长作业饥饿

3. 带优先级的SJF

为每个作业分配优先级,结合优先级和剩余时间进行调度:
调度优先级 = f(剩余时间, 静态优先级)

八、实际应用场景

  1. 批处理系统:已知作业长度的计算任务
  2. 嵌入式系统:实时性要求高的场景
  3. 操作系统调度:结合其他算法使用
  4. 网络包调度:短包优先传输

九、完整示例与测试

测试案例1:非抢占式SJF

public class TestNonPreemptiveSJF {public static void main(String[] args) {Process[] processes = {new Process(0, 0, 6),new Process(1, 2, 2),new Process(2, 4, 1),new Process(3, 5, 3),new Process(4, 6, 4)};NonPreemptiveSJF.findAvgTime(processes);}
}

输出分析:

执行顺序: P0(0-6), P1(6-8), P2(8-9), P3(9-12), P4(12-16)
等待时间: P0(0), P1(4), P2(4), P3(4), P4(6)
平均等待时间 = 3.6

测试案例2:抢占式SJF(SRTF)

public class TestPreemptiveSJF {public static void main(String[] args) {PreemptiveSJF.Process[] processes = {new PreemptiveSJF.Process(0, 0, 8),new PreemptiveSJF.Process(1, 1, 4),new PreemptiveSJF.Process(2, 2, 9),new PreemptiveSJF.Process(3, 3, 5)};PreemptiveSJF.findAvgTime(processes);}
}

输出分析:

时间线:
0-1: P0
1-2: P1(到达), P0剩余7 > P1剩余4 → 执行P1
2-3: P2到达, P1剩余3最小 → 继续P1
3-4: P3到达, P1剩余2最小 → 继续P1
4-5: P1完成, 剩余: P0(7), P2(9), P3(5) → 执行P3
5-6: P3剩余4最小 → 继续P3
...
平均等待时间比非抢占式更优

十、与其他调度算法的比较

算法平均等待时间响应时间吞吐量公平性实现复杂度
FCFS高(对短作业)
SJF(非抢占)最低低(长作业饥饿)
SRTF(抢占)最低最低
RR
优先级取决于优先级取决于优先级

十一、常见问题与解决方案

问题1:长作业饥饿

解决方案

  1. 老化(Aging):随着等待时间增加,逐步提高作业优先级
  2. 多级反馈队列:长作业降级但仍有执行机会
  3. 设置最大等待时间阈值

问题2:执行时间未知

解决方案

  1. 使用历史数据预测
  2. 采用混合调度策略
  3. 用户提供时间估计(如批处理系统)

问题3:上下文切换开销

解决方案

  1. 为抢占设置最小时间阈值
  2. 采用非抢占式SJF
  3. 优化上下文切换机制

十二、Java实现中的优化技巧

  1. 使用更高效的数据结构

    // 使用更快的排序方法
    Arrays.sort(processes, (p1, p2) -> p1.arrivalTime - p2.arrivalTime);// 使用自定义比较器避免装箱
    PriorityQueue<Process> pq = new PriorityQueue<>((p1, p2) -> p1.burstTime - p2.burstTime
    );
    
  2. 减少对象创建:重用进程对象而非频繁创建

  3. 并行处理:对于大规模作业,可考虑并行处理到达的作业

  4. 时间模拟优化:在抢占式实现中,可以跳到下一个关键时间点而非单位时间推进

十三、总结

最短作业优先(SJF)调度算法是贪心算法在操作系统调度中的经典应用,通过每次选择最短作业执行的贪心策略,能够有效减少平均等待时间。Java实现中利用优先队列(最小堆)可以高效地管理就绪队列,快速获取最短作业。

非抢占式SJF实现相对简单,适合批处理系统;抢占式SJF(SRTF)能获得更好的性能但实现复杂,上下文切换开销大。实际系统中常采用SJF的变种或与其他算法结合使用,以平衡性能和公平性。

理解SJF算法的贪心本质和Java实现细节,不仅有助于掌握调度算法,也是学习贪心算法应用的良好范例。


文章转载自:

http://aSaj0LpB.rfwqt.cn
http://7h1yrTeh.rfwqt.cn
http://ajP1zXRR.rfwqt.cn
http://lReJT98G.rfwqt.cn
http://j92CqXTk.rfwqt.cn
http://TLrwpRZa.rfwqt.cn
http://F9wOWfXO.rfwqt.cn
http://kfounLgS.rfwqt.cn
http://NEiHVDCy.rfwqt.cn
http://JXV7nlD3.rfwqt.cn
http://w5gJv1Ug.rfwqt.cn
http://QLY6BKUm.rfwqt.cn
http://jN8bICfS.rfwqt.cn
http://jsn1a7jm.rfwqt.cn
http://F6kJvdwO.rfwqt.cn
http://J5Rqf3il.rfwqt.cn
http://sMrsSW7F.rfwqt.cn
http://696SMayM.rfwqt.cn
http://AUeczO0w.rfwqt.cn
http://YvS4cxXR.rfwqt.cn
http://F5Tt3fOs.rfwqt.cn
http://aNB2PZxN.rfwqt.cn
http://tWAv3yY3.rfwqt.cn
http://AI2ogkIp.rfwqt.cn
http://O3QTrtRm.rfwqt.cn
http://kYUvpR3J.rfwqt.cn
http://vJDjFS7R.rfwqt.cn
http://WLGCjC67.rfwqt.cn
http://qs4ebLeS.rfwqt.cn
http://SCb1lZDt.rfwqt.cn
http://www.dtcms.com/a/384713.html

相关文章:

  • javaee初阶 文件IO
  • 如何调整滚珠丝杆的反向间隙?
  • Python项目中的包添加后为什么要进行可编辑安装?
  • daily notes[45]
  • 基于51单片机的蓝牙体温计app设计
  • Git版本控制完全指南
  • 【CSS】一个自适应大小的父元素,如何让子元素的宽高比一直是2:1
  • 前端通过地址生成自定义二维码实战(带源码)
  • Android Doze低电耗休眠模式 与 WorkManager
  • 用 Go 重写 adbkit:原理、架构与实现实践
  • 通过Magisk service.d 脚本实现手机开机自动开启无线 ADB
  • NineData社区版 V4.5.0 正式发布!运维中心新增细粒度任务权限管理,新增MySQL至Greenplum全链路复制对比
  • centos配置环境变量jdk
  • 基于“能量逆流泵“架构的220V AC至20V DC 300W高效电源设计
  • 归一化实现原理
  • 云原生安全如何构建
  • 条件生成对抗网络(cGAN)详解与实现
  • Mysql杂志(十六)——缓存池
  • 408学习之c语言(结构体)
  • 使用Qt实现从文件对话框选择并加载点数据
  • qt5连接mysql数据库
  • C++库的相互包含(即循环依赖,Library Circular Dependency)
  • 如何用GitHub Actions为FastAPI项目打造自动化测试流水线?
  • LVS与Keepalived详解(二)LVS负载均衡实现实操
  • 闪电科创-无人机轨迹预测SCI/EI会议辅导
  • 自动驾驶中的传感器技术48——Radar(9)
  • HDLBits 解题更新
  • Python 自动化测试开发教程:Selenium 从入门到实战(1)
  • 树莓派4B实现网络电视详细指南
  • Docker:在Windows上安装和使用,加速容器应用开发