Add disk scheduling algorithms (#5748)

This commit is contained in:
xuyang471
2024-10-14 16:22:30 +08:00
committed by GitHub
parent e9b897bdeb
commit 85b3b1dfbe
10 changed files with 674 additions and 0 deletions

View File

@ -0,0 +1,80 @@
package com.thealgorithms.scheduling.diskscheduling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Circular Look Scheduling (C-LOOK) is a disk scheduling algorithm similar to
* the C-SCAN algorithm but with a key difference. In C-LOOK, the disk arm also
* moves in one direction to service requests, but instead of going all the way
* to the end of the disk, it only goes as far as the furthest request in the
* current direction. After servicing the last request in the current direction,
* the arm immediately jumps back to the closest request on the other side without
* moving to the disk's extreme ends. This reduces the unnecessary movement of the
* disk arm, resulting in better performance compared to C-SCAN, while still
* maintaining fair wait times for requests.
*/
public class CircularLookScheduling {
private int currentPosition;
private boolean movingUp;
private final int maxCylinder;
public CircularLookScheduling(int startPosition, boolean movingUp, int maxCylinder) {
this.currentPosition = startPosition;
this.movingUp = movingUp;
this.maxCylinder = maxCylinder;
}
public List<Integer> execute(List<Integer> requests) {
List<Integer> result = new ArrayList<>();
// Filter and sort valid requests in both directions
List<Integer> upRequests = new ArrayList<>();
List<Integer> downRequests = new ArrayList<>();
for (int request : requests) {
if (request >= 0 && request < maxCylinder) {
if (request > currentPosition) {
upRequests.add(request);
} else if (request < currentPosition) {
downRequests.add(request);
}
}
}
Collections.sort(upRequests);
Collections.sort(downRequests);
if (movingUp) {
// Process all requests in the upward direction
result.addAll(upRequests);
// Jump to the lowest request and process all requests in the downward direction
result.addAll(downRequests);
} else {
// Process all requests in the downward direction (in reverse order)
Collections.reverse(downRequests);
result.addAll(downRequests);
// Jump to the highest request and process all requests in the upward direction (in reverse order)
Collections.reverse(upRequests);
result.addAll(upRequests);
}
// Update current position to the last processed request
if (!result.isEmpty()) {
currentPosition = result.get(result.size() - 1);
}
return result;
}
public int getCurrentPosition() {
return currentPosition;
}
public boolean isMovingUp() {
return movingUp;
}
}

View File

@ -0,0 +1,83 @@
package com.thealgorithms.scheduling.diskscheduling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Circular Scan Scheduling (C-SCAN) is a disk scheduling algorithm that
* works by moving the disk arm in one direction to service requests until
* it reaches the end of the disk. Once it reaches the end, instead of reversing
* direction like in the SCAN algorithm, the arm moves back to the starting point
* without servicing any requests. This ensures a more uniform wait time for all
* requests, especially those near the disk edges. The algorithm then continues in
* the same direction, making it effective for balancing service time across all disk sectors.
*/
public class CircularScanScheduling {
private int currentPosition;
private boolean movingUp;
private final int diskSize;
public CircularScanScheduling(int startPosition, boolean movingUp, int diskSize) {
this.currentPosition = startPosition;
this.movingUp = movingUp;
this.diskSize = diskSize;
}
public List<Integer> execute(List<Integer> requests) {
if (requests.isEmpty()) {
return new ArrayList<>(); // Return empty list if there are no requests
}
List<Integer> sortedRequests = new ArrayList<>(requests);
Collections.sort(sortedRequests);
List<Integer> result = new ArrayList<>();
if (movingUp) {
// Moving up: process requests >= current position
for (int request : sortedRequests) {
if (request >= currentPosition && request < diskSize) {
result.add(request);
}
}
// Jump to the smallest request and continue processing from the start
for (int request : sortedRequests) {
if (request < currentPosition) {
result.add(request);
}
}
} else {
// Moving down: process requests <= current position in reverse order
for (int i = sortedRequests.size() - 1; i >= 0; i--) {
int request = sortedRequests.get(i);
if (request <= currentPosition) {
result.add(request);
}
}
// Jump to the largest request and continue processing in reverse order
for (int i = sortedRequests.size() - 1; i >= 0; i--) {
int request = sortedRequests.get(i);
if (request > currentPosition) {
result.add(request);
}
}
}
// Set final position to the last request processed
if (!result.isEmpty()) {
currentPosition = result.get(result.size() - 1);
}
return result;
}
public int getCurrentPosition() {
return currentPosition;
}
public boolean isMovingUp() {
return movingUp;
}
}

View File

@ -0,0 +1,95 @@
package com.thealgorithms.scheduling.diskscheduling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* https://en.wikipedia.org/wiki/LOOK_algorithm
* Look Scheduling algorithm implementation.
* The Look algorithm moves the disk arm to the closest request in the current direction,
* and once it processes all requests in that direction, it reverses the direction.
*/
public class LookScheduling {
private final int maxTrack;
private final int currentPosition;
private boolean movingUp;
private int farthestPosition;
public LookScheduling(int startPosition, boolean initialDirection, int maxTrack) {
this.currentPosition = startPosition;
this.movingUp = initialDirection;
this.maxTrack = maxTrack;
}
/**
* Executes the Look Scheduling algorithm on the given list of requests.
*
* @param requests List of disk requests.
* @return Order in which requests are processed.
*/
public List<Integer> execute(List<Integer> requests) {
List<Integer> result = new ArrayList<>();
List<Integer> lower = new ArrayList<>();
List<Integer> upper = new ArrayList<>();
// Split requests into two lists based on their position relative to current position
for (int request : requests) {
if (request >= 0 && request < maxTrack) {
if (request < currentPosition) {
lower.add(request);
} else {
upper.add(request);
}
}
}
// Sort the requests
Collections.sort(lower);
Collections.sort(upper);
// Process the requests depending on the initial moving direction
if (movingUp) {
// Process requests in the upward direction
result.addAll(upper);
if (!upper.isEmpty()) {
farthestPosition = upper.get(upper.size() - 1);
}
// Reverse the direction and process downward
movingUp = false;
Collections.reverse(lower);
result.addAll(lower);
if (!lower.isEmpty()) {
farthestPosition = Math.max(farthestPosition, lower.get(0));
}
} else {
// Process requests in the downward direction
Collections.reverse(lower);
result.addAll(lower);
if (!lower.isEmpty()) {
farthestPosition = lower.get(0);
}
// Reverse the direction and process upward
movingUp = true;
result.addAll(upper);
if (!upper.isEmpty()) {
farthestPosition = Math.max(farthestPosition, upper.get(upper.size() - 1));
}
}
return result;
}
public int getCurrentPosition() {
return currentPosition;
}
public boolean isMovingUp() {
return movingUp;
}
public int getFarthestPosition() {
return farthestPosition;
}
}

View File

@ -0,0 +1,56 @@
package com.thealgorithms.scheduling.diskscheduling;
import java.util.ArrayList;
import java.util.List;
/**
*https://en.wikipedia.org/wiki/Shortest_seek_first
* Shortest Seek First (SFF) Scheduling algorithm implementation.
* The SFF algorithm selects the next request to be serviced based on the shortest distance
* from the current position of the disk arm. It continuously evaluates all pending requests
* and chooses the one that requires the least amount of movement to service.
*
* This approach minimizes the average seek time, making it efficient in terms of response
* time for individual requests. However, it may lead to starvation for requests located
* further away from the current position of the disk arm.
*
* The SFF algorithm is particularly effective in systems where quick response time
* is crucial, as it ensures that the most accessible requests are prioritized for servicing.
*/
public class SSFScheduling {
private int currentPosition;
public SSFScheduling(int currentPosition) {
this.currentPosition = currentPosition;
}
public List<Integer> execute(List<Integer> requests) {
List<Integer> result = new ArrayList<>(requests);
List<Integer> orderedRequests = new ArrayList<>();
while (!result.isEmpty()) {
int closest = findClosest(result);
orderedRequests.add(closest);
result.remove(Integer.valueOf(closest));
currentPosition = closest;
}
return orderedRequests;
}
private int findClosest(List<Integer> requests) {
int minDistance = Integer.MAX_VALUE;
int closest = -1;
for (int request : requests) {
int distance = Math.abs(currentPosition - request);
if (distance < minDistance) {
minDistance = distance;
closest = request;
}
}
return closest;
}
public int getCurrentPosition() {
return currentPosition;
}
}

View File

@ -0,0 +1,82 @@
package com.thealgorithms.scheduling.diskscheduling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* https://en.wikipedia.org/wiki/Elevator_algorithm
* SCAN Scheduling algorithm implementation.
* The SCAN algorithm moves the disk arm towards one end of the disk, servicing all requests
* along the way until it reaches the end. Once it reaches the end, it reverses direction
* and services the requests on its way back.
*
* This algorithm ensures that all requests are serviced in a fair manner,
* while minimizing the seek time for requests located close to the current position
* of the disk arm.
*
* The SCAN algorithm is particularly useful in environments with a large number of
* disk requests, as it reduces the overall movement of the disk arm compared to
*/
public class ScanScheduling {
private int headPosition;
private int diskSize;
private boolean movingUp;
public ScanScheduling(int headPosition, boolean movingUp, int diskSize) {
this.headPosition = headPosition;
this.movingUp = movingUp;
this.diskSize = diskSize;
}
public List<Integer> execute(List<Integer> requests) {
// If the request list is empty, return an empty result
if (requests.isEmpty()) {
return new ArrayList<>();
}
List<Integer> result = new ArrayList<>();
List<Integer> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
// Separate requests into those smaller than the current head position and those larger
for (int request : requests) {
if (request < headPosition) {
left.add(request);
} else {
right.add(request);
}
}
// Sort the requests
Collections.sort(left);
Collections.sort(right);
// Simulate the disk head movement
if (movingUp) {
// Head moving upward, process right-side requests first
result.addAll(right);
// After reaching the end of the disk, reverse direction and process left-side requests
result.add(diskSize - 1); // Simulate the head reaching the end of the disk
Collections.reverse(left);
result.addAll(left);
} else {
// Head moving downward, process left-side requests first
Collections.reverse(left);
result.addAll(left);
// After reaching the start of the disk, reverse direction and process right-side requests
result.add(0); // Simulate the head reaching the start of the disk
result.addAll(right);
}
return result;
}
public int getHeadPosition() {
return headPosition;
}
public boolean isMovingUp() {
return movingUp;
}
}

View File

@ -0,0 +1,54 @@
package com.thealgorithms.scheduling.diskscheduling;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class CircularLookSchedulingTest {
@Test
public void testCircularLookSchedulingMovingUp() {
CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 18, 39);
List<Integer> result = scheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testCircularLookSchedulingMovingDown() {
CircularLookScheduling scheduling = new CircularLookScheduling(50, false, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(39, 18, 160, 150, 90, 58, 55);
List<Integer> result = scheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testCircularLookSchedulingEmptyRequests() {
CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList();
List<Integer> expected = Arrays.asList();
List<Integer> result = scheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testCircularLookSchedulingPrintStatus() {
CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> result = scheduling.execute(requests);
// Print the final status
System.out.println("Final CircularLookScheduling Position: " + scheduling.getCurrentPosition());
System.out.println("CircularLookScheduling Moving Up: " + scheduling.isMovingUp());
// Print the order of request processing
System.out.println("Request Order: " + result);
}
}

View File

@ -0,0 +1,48 @@
package com.thealgorithms.scheduling.diskscheduling;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class CircularScanSchedulingTest {
@Test
public void testCircularScanSchedulingMovingUp() {
CircularScanScheduling circularScan = new CircularScanScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 18, 39);
List<Integer> result = circularScan.execute(requests);
assertEquals(expectedOrder, result);
System.out.println("Final CircularScan Position: " + circularScan.getCurrentPosition());
System.out.println("CircularScan Moving Up: " + circularScan.isMovingUp());
System.out.println("Request Order: " + result);
}
@Test
public void testCircularScanSchedulingMovingDown() {
CircularScanScheduling circularScan = new CircularScanScheduling(50, false, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expectedOrder = Arrays.asList(39, 18, 160, 150, 90, 58, 55);
List<Integer> result = circularScan.execute(requests);
assertEquals(expectedOrder, result);
System.out.println("Final CircularScan Position: " + circularScan.getCurrentPosition());
System.out.println("CircularScan Moving Down: " + circularScan.isMovingUp());
System.out.println("Request Order: " + result);
}
@Test
public void testCircularScanSchedulingEmptyRequests() {
CircularScanScheduling circularScan = new CircularScanScheduling(50, true, 200);
List<Integer> requests = Arrays.asList();
List<Integer> expectedOrder = Arrays.asList();
List<Integer> result = circularScan.execute(requests);
assertEquals(expectedOrder, result);
}
}

View File

@ -0,0 +1,67 @@
package com.thealgorithms.scheduling.diskscheduling;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class LookSchedulingTest {
@Test
public void testLookSchedulingMovingUp() {
LookScheduling lookScheduling = new LookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 39, 18);
List<Integer> result = lookScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testLookSchedulingMovingDown() {
LookScheduling lookScheduling = new LookScheduling(50, false, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(39, 18, 55, 58, 90, 150, 160);
List<Integer> result = lookScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testLookSchedulingEmptyRequests() {
LookScheduling lookScheduling = new LookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList();
List<Integer> expected = Arrays.asList();
List<Integer> result = lookScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testLookSchedulingCurrentPosition() {
LookScheduling lookScheduling = new LookScheduling(50, true, 200);
// Testing current position remains unchanged after scheduling.
assertEquals(50, lookScheduling.getCurrentPosition());
}
@Test
public void testLookSchedulingPrintStatus() {
LookScheduling lookScheduling = new LookScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> result = lookScheduling.execute(requests);
List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 39, 18);
assertEquals(expectedOrder, result);
System.out.println("Final LookScheduling Position: " + lookScheduling.getCurrentPosition());
System.out.println("LookScheduling Moving Up: " + lookScheduling.isMovingUp());
System.out.println("Farthest Position Reached: " + lookScheduling.getFarthestPosition());
System.out.println("Request Order: " + result);
}
}

View File

@ -0,0 +1,55 @@
package com.thealgorithms.scheduling.diskscheduling;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class SSFSchedulingTest {
private SSFScheduling scheduler;
@BeforeEach
public void setUp() {
scheduler = new SSFScheduling(50);
}
@Test
public void testExecuteWithEmptyList() {
List<Integer> requests = new ArrayList<>();
List<Integer> result = scheduler.execute(requests);
assertTrue(result.isEmpty(), "Result should be empty for an empty request list.");
}
@Test
public void testExecuteWithSingleRequest() {
List<Integer> requests = new ArrayList<>(List.of(100));
List<Integer> result = scheduler.execute(requests);
assertEquals(List.of(100), result, "The only request should be served first.");
}
@Test
public void testExecuteWithMultipleRequests() {
List<Integer> requests = new ArrayList<>(List.of(10, 90, 60, 40, 30, 70));
List<Integer> result = scheduler.execute(requests);
assertEquals(List.of(60, 70, 90, 40, 30, 10), result, "Requests should be served in the shortest seek first order.");
}
@Test
public void testExecuteWithSameDistanceRequests() {
List<Integer> requests = new ArrayList<>(List.of(45, 55));
List<Integer> result = scheduler.execute(requests);
assertEquals(List.of(45, 55), result, "When distances are equal, requests should be served in the order they appear in the list.");
}
@Test
public void testGetCurrentPositionAfterExecution() {
List<Integer> requests = new ArrayList<>(List.of(10, 90, 60, 40, 30, 70));
scheduler.execute(requests);
int currentPosition = scheduler.getCurrentPosition();
assertEquals(10, currentPosition, "Current position should be the last request after execution.");
}
}

View File

@ -0,0 +1,54 @@
package com.thealgorithms.scheduling.diskscheduling;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class ScanSchedulingTest {
@Test
public void testScanSchedulingMovingUp() {
ScanScheduling scanScheduling = new ScanScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 199, 39, 18);
List<Integer> result = scanScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testScanSchedulingMovingDown() {
ScanScheduling scanScheduling = new ScanScheduling(50, false, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> expected = Arrays.asList(39, 18, 0, 55, 58, 90, 150, 160);
List<Integer> result = scanScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testScanSchedulingEmptyRequests() {
ScanScheduling scanScheduling = new ScanScheduling(50, true, 200);
List<Integer> requests = Arrays.asList();
List<Integer> expected = Arrays.asList();
List<Integer> result = scanScheduling.execute(requests);
assertEquals(expected, result);
}
@Test
public void testScanScheduling() {
ScanScheduling scanScheduling = new ScanScheduling(50, true, 200);
List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150);
List<Integer> result = scanScheduling.execute(requests);
List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 199, 39, 18);
assertEquals(expectedOrder, result);
System.out.println("Final Head Position: " + scanScheduling.getHeadPosition());
System.out.println("Head Moving Up: " + scanScheduling.isMovingUp());
System.out.println("Request Order: " + result);
}
}