最短作业优先(SJF)或紧随其后的最短作业是一种调度策略,它选择执行时间最短的等待进程来执行。 SJN是一种非抢占式算法。
- 最短作业优先的优点是在所有调度算法中平均等待时间最短。
- 这是一个贪婪算法。
- 如果持续出现较短的过程,可能会导致饥饿。使用老化的概念可以解决这个问题。
- 这实际上是不可行的,因为操作系统可能不知道突发时间,因此可能无法对它们进行排序。尽管无法预测执行时间,但是可以使用几种方法来估计作业的执行时间,例如先前执行时间的加权平均值。 SJF可用于可以准确估计运行时间的特殊环境中。
例如:
在上面的示例中,由于所有进程的到达时间均为0,因此进程的执行顺序为进程的突发时间的升序。突发时间由列持续时间给出。因此,流程的执行顺序由下式给出:
P4 -> P1 -> P3 -> P2
这篇文章已经在朴素方法的帮助下讨论了该算法的一种实现。在本文中,该算法是通过使用段树的概念来实现的。
方法:以下是首先执行最短作业的方法:
- 顾名思义,最短作业优先算法是执行突发时间最少且已在当前时间之前到达的过程的算法。因此,为了找到需要执行的过程,请根据它们的到达时间从给定的过程集中对所有过程进行排序。这样可以确保首先执行突发时间最短的过程。
- 而不是通过迭代来找到所有到达进程中的最小突发时间进程
在整个struct数组中,使用段树计算所有到达进程的突发时间到当前时间的最小范围。 - 选择了需要执行的处理后,使用处理的到达时间和突发时间来计算完成时间,周转时间和等待时间。计算各个时间的公式为:
- 完成时间:流程完成其执行的时间。
Completion Time = Start Time + Burst Time
- 周转时间:完成时间与到达时间之间的时间差。
Turn Around Time = Completion Time – Arrival Time
- Waiting Time(WT):等待时间和突发时间之间的时间差。
Waiting Time = Turn Around Time – Burst Time
- 完成时间:流程完成其执行的时间。
- 在计算之后,在阵列中更新各个时间,并且在段树基数组中将执行过程的突发时间设置为无穷大,以便在其他查询中将其不视为最小突发时间。
下面是使用段树概念首先实现最短作业的方法:
C++
// C++ implementation of shortest job first
// using the concept of segment tree
#include
using namespace std;
#define ll long long
#define z 1000000007
#define sh 100000
#define pb push_back
#define pr(x) printf("%d ", x)
struct util {
// Process ID
int id;
// Arrival time
int at;
// Burst time
int bt;
// Completion time
int ct;
// Turnaround time
int tat;
// Waiting time
int wt;
}
// Array to store all the process information
// by implementing the above struct util
ar[sh + 1];
struct util1 {
// Process id
int p_id;
// burst time
int bt1;
};
util1 range;
// Segment tree array to
// process the queries in nlogn
util1 tr[4 * sh + 5];
// To keep an account of where
// a particular process_id is
// in the segment tree base array
int mp[sh + 1];
// Comparator function to sort the
// struct array according to arrival time
bool cmp(util a, util b)
{
if (a.at == b.at)
return a.id < b.id;
return a.at < b.at;
}
// Function to update the burst time and process id
// in the segment tree
void update(int node, int st, int end,
int ind, int id1, int b_t)
{
if (st == end) {
tr[node].p_id = id1;
tr[node].bt1 = b_t;
return;
}
int mid = (st + end) / 2;
if (ind <= mid)
update(2 * node, st, mid, ind, id1, b_t);
else
update(2 * node + 1, mid + 1, end, ind, id1, b_t);
if (tr[2 * node].bt1 < tr[2 * node + 1].bt1) {
tr[node].bt1 = tr[2 * node].bt1;
tr[node].p_id = tr[2 * node].p_id;
}
else {
tr[node].bt1 = tr[2 * node + 1].bt1;
tr[node].p_id = tr[2 * node + 1].p_id;
}
}
// Function to return the range minimum of the burst time
// of all the arrived processes using segment tree
util1 query(int node, int st, int end, int lt, int rt)
{
if (end < lt || st > rt)
return range;
if (st >= lt && end <= rt)
return tr[node];
int mid = (st + end) / 2;
util1 lm = query(2 * node, st, mid, lt, rt);
util1 rm = query(2 * node + 1, mid + 1, end, lt, rt);
if (lm.bt1 < rm.bt1)
return lm;
return rm;
}
// Function to perform non_preemptive
// shortest job first and return the
// completion time, turn around time and
// waiting time for the given processes
void non_premptive_sjf(int n)
{
// To store the number of processes
// that have been completed
int counter = n;
// To keep an account of the number
// of processes that have been arrived
int upper_range = 0;
// Current running time
int tm = min(INT_MAX, ar[upper_range + 1].at);
// To find the list of processes whose arrival time
// is less than or equal to the current time
while (counter) {
for (; upper_range <= n;) {
upper_range++;
if (ar[upper_range].at > tm || upper_range > n) {
upper_range--;
break;
}
update(1, 1, n, upper_range,
ar[upper_range].id, ar[upper_range].bt);
}
// To find the minimum of all the running times
// from the set of processes whose arrival time is
// less than or equal to the current time
util1 res = query(1, 1, n, 1, upper_range);
// Checking if the process has already been executed
if (res.bt1 != INT_MAX) {
counter--;
int index = mp[res.p_id];
tm += (res.bt1);
// Calculating and updating the array with
// the current time, turn around time and waiting time
ar[index].ct = tm;
ar[index].tat = ar[index].ct - ar[index].at;
ar[index].wt = ar[index].tat - ar[index].bt;
// Update the process burst time with
// infinity when the process is executed
update(1, 1, n, index, INT_MAX, INT_MAX);
}
else {
tm = ar[upper_range + 1].at;
}
}
}
// Function to call the functions and perform
// shortest job first operation
void execute(int n)
{
// Sort the array based on the arrival times
sort(ar + 1, ar + n + 1, cmp);
for (int i = 1; i <= n; i++)
mp[ar[i].id] = i;
// Calling the function to perform
// non-premptive-sjf
non_premptive_sjf(n);
}
// Function to print the required values after
// performing shortest job first
void print(int n)
{
cout << "ProcessId "
<< "Arrival Time "
<< "Burst Time "
<< "Completion Time "
<< "Turn Around Time "
<< "Waiting Time\n";
for (int i = 1; i <= n; i++) {
cout << ar[i].id << " \t\t "
<< ar[i].at << " \t\t "
<< ar[i].bt << " \t\t "
<< ar[i].ct << " \t\t "
<< ar[i].tat << " \t\t "
<< ar[i].wt << " \n";
}
}
// Driver code
int main()
{
// Number of processes
int n = 5;
// Initializing the process id
// and burst time
range.p_id = INT_MAX;
range.bt1 = INT_MAX;
for (int i = 1; i <= 4 * sh + 1; i++) {
tr[i].p_id = INT_MAX;
tr[i].bt1 = INT_MAX;
}
// Arrival time, Burst time and ID
// of the processes on which SJF needs
// to be performed
ar[1].at = 1;
ar[1].bt = 7;
ar[1].id = 1;
ar[2].at = 2;
ar[2].bt = 5;
ar[2].id = 2;
ar[3].at = 3;
ar[3].bt = 1;
ar[3].id = 3;
ar[4].at = 4;
ar[4].bt = 2;
ar[4].id = 4;
ar[5].at = 5;
ar[5].bt = 8;
ar[5].id = 5;
execute(n);
// Print the calculated time
print(n);
}
Java
// Java implementation of shortest job first
// using the concept of segment tree
import java.util.*;
class GFG {
static int z = 1000000007;
static int sh = 100000;
static class util {
// Process ID
int id;
// Arrival time
int at;
// Burst time
int bt;
// Completion time
int ct;
// Turnaround time
int tat;
// Waiting time
int wt;
}
// Array to store all the process information
// by implementing the above struct util
static util[] ar = new util[sh + 1];
static {
for (int i = 0; i < sh + 1; i++) {
ar[i] = new util();
}
}
static class util1 {
// Process id
int p_id;
// burst time
int bt1;
};
static util1 range = new util1();
// Segment tree array to
// process the queries in nlogn
static util1[] tr = new util1[4 * sh + 5];
static {
for (int i = 0; i < 4 * sh + 5; i++) {
tr[i] = new util1();
}
}
// To keep an account of where
// a particular process_id is
// in the segment tree base array
static int[] mp = new int[sh + 1];
// Comparator function to sort the
// struct array according to arrival time
// Function to update the burst time and process id
// in the segment tree
static void update(int node, int st, int end,
int ind, int id1, int b_t)
{
if (st == end) {
tr[node].p_id = id1;
tr[node].bt1 = b_t;
return;
}
int mid = (st + end) / 2;
if (ind <= mid)
update(2 * node, st, mid, ind, id1, b_t);
else
update(2 * node + 1, mid + 1, end, ind, id1, b_t);
if (tr[2 * node].bt1 < tr[2 * node + 1].bt1) {
tr[node].bt1 = tr[2 * node].bt1;
tr[node].p_id = tr[2 * node].p_id;
} else {
tr[node].bt1 = tr[2 * node + 1].bt1;
tr[node].p_id = tr[2 * node + 1].p_id;
}
}
// Function to return the range minimum of the burst time
// of all the arrived processes using segment tree
static util1 query(int node, int st, int end,
int lt, int rt)
{
if (end < lt || st > rt)
return range;
if (st >= lt && end <= rt)
return tr[node];
int mid = (st + end) / 2;
util1 lm = query(2 * node, st, mid, lt, rt);
util1 rm = query(2 * node + 1, mid + 1, end, lt, rt);
if (lm.bt1 < rm.bt1)
return lm;
return rm;
}
// Function to perform non_preemptive
// shortest job first and return the
// completion time, turn around time and
// waiting time for the given processes
static void non_premptive_sjf(int n) {
// To store the number of processes
// that have been completed
int counter = n;
// To keep an account of the number
// of processes that have been arrived
int upper_range = 0;
// Current running time
int tm = Math.min(Integer.MAX_VALUE, ar[upper_range + 1].at);
// To find the list of processes whose arrival time
// is less than or equal to the current time
while (counter != 0) {
for (; upper_range <= n;) {
upper_range++;
if (ar[upper_range].at > tm || upper_range > n) {
upper_range--;
break;
}
update(1, 1, n, upper_range, ar[upper_range].id,
ar[upper_range].bt);
}
// To find the minimum of all the running times
// from the set of processes whose arrival time is
// less than or equal to the current time
util1 res = query(1, 1, n, 1, upper_range);
// Checking if the process has already been executed
if (res.bt1 != Integer.MAX_VALUE) {
counter--;
int index = mp[res.p_id];
tm += (res.bt1);
// Calculating and updating the array with
// the current time, turn around time and waiting time
ar[index].ct = tm;
ar[index].tat = ar[index].ct - ar[index].at;
ar[index].wt = ar[index].tat - ar[index].bt;
// Update the process burst time with
// infinity when the process is executed
update(1, 1, n, index, Integer.MAX_VALUE, Integer.MAX_VALUE);
} else {
tm = ar[upper_range + 1].at;
}
}
}
// Function to call the functions and perform
// shortest job first operation
static void execute(int n) {
// Sort the array based on the arrival times
Arrays.sort(ar, 1, n, new Comparator() {
public int compare(util a, util b) {
if (a.at == b.at)
return a.id - b.id;
return a.at - b.at;
}
});
for (int i = 1; i <= n; i++)
mp[ar[i].id] = i;
// Calling the function to perform
// non-premptive-sjf
non_premptive_sjf(n);
}
// Function to print the required values after
// performing shortest job first
static void print(int n) {
System.out.println("ProcessId Arrival Time Burst Time" +
" Completion Time Turn Around Time Waiting Time");
for (int i = 1; i <= n; i++) {
System.out.printf("%d\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\n",
ar[i].id, ar[i].at, ar[i].bt, ar[i].ct, ar[i].tat,
ar[i].wt);
}
}
// Driver Code
public static void main(String[] args)
{
// Number of processes
int n = 5;
// Initializing the process id
// and burst time
range.p_id = Integer.MAX_VALUE;
range.bt1 = Integer.MAX_VALUE;
for (int i = 1; i <= 4 * sh + 1; i++)
{
tr[i].p_id = Integer.MAX_VALUE;
tr[i].bt1 = Integer.MAX_VALUE;
}
// Arrival time, Burst time and ID
// of the processes on which SJF needs
// to be performed
ar[1].at = 1;
ar[1].bt = 7;
ar[1].id = 1;
ar[2].at = 2;
ar[2].bt = 5;
ar[2].id = 2;
ar[3].at = 3;
ar[3].bt = 1;
ar[3].id = 3;
ar[4].at = 4;
ar[4].bt = 2;
ar[4].id = 4;
ar[5].at = 5;
ar[5].bt = 8;
ar[5].id = 5;
execute(n);
// Print the calculated time
print(n);
}
}
// This code is contributed by
// sanjeev2552
输出:
ProcessId Arrival Time Burst Time Completion Time Turn Around Time Waiting Time
1 1 7 8 7 0
2 2 5 16 14 9
3 3 1 9 6 5
4 4 2 11 7 5
5 5 8 24 19 11
时间复杂度:为了分析上述算法的运行时间,首先需要了解以下运行时间:
- 构造用于N个进程的分段树的时间复杂度为O(N) 。
- O(log(N))给出了更新段树中节点的时间复杂度。
- 由O(log(N))给出在段树中执行范围最小查询的时间复杂度。
- 由于更新操作和查询是针对给定的N个进程执行的,因此该算法的总时间复杂度为O(N * log(N)) ,其中N为进程数。
- 该算法的性能比本文中提到的方法更好,因为它需要O(N 2 )来执行。