package queuingSimulation; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.Scanner; public class Simulation { static final Job jobWhichNeverArrives = new Job(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); public static void main(String[] args) throws IOException { Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in))); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); int N = in.nextInt(); Job[] job = new Job[N]; double lambda = in.nextDouble(); double mu = in.nextDouble(); // 0 if QueueSize is unbounded int maxQueueSize = in.nextInt(); if (maxQueueSize == 0) { maxQueueSize = Integer.MAX_VALUE; } Random rnd = new Random(); double arrivalTime = nextRandomExponential(rnd, lambda); double serviceTime = nextRandomExponential(rnd, mu); job[0] = new Job(0, arrivalTime, serviceTime); for (int i = 1; i < N; i++) { /* * Inter arrival times are exponentially distributed. Service time * is exponentially distributed. We want to generate random numbers * which follow exponential distribution. The default random number * generated follow uniform distribution. We need to convert that * distribution into exponential distribution. */ arrivalTime = nextRandomExponential(rnd, lambda) + job[i - 1].arrivalTime; serviceTime = nextRandomExponential(rnd, mu); job[i] = new Job(i, arrivalTime, serviceTime); } in.close(); Queue<Job> waitingQueue = new LinkedList<Job>(); ArrayList<Double> qltGraphQueueLength = new ArrayList<Double>(); ArrayList<Double> qltGraphTime = new ArrayList<Double>(); ArrayList<Double> ttGraphThroughput = new ArrayList<Double>(); ArrayList<Double> ttGraphTime = new ArrayList<Double>(); double numberOfJobs = 0.0; double totalServiceTime = 0.0; qltGraphQueueLength.add(0.0); qltGraphTime.add(0.0); ttGraphThroughput.add(0.0); ttGraphTime.add(0.0); double epsilon = 0.0001; Job currentJob = job[0]; Job nextJob = job[1]; double currentJobDepartureTime = job[0].arrivalTime + job[0].serviceTime; double QiTi = 0; double lastUpdate = 0; while (true) { if (nextJob.arrivalTime < currentJobDepartureTime) { // Next event is an arrival. QiTi += (waitingQueue.size() * (nextJob.arrivalTime - lastUpdate)); lastUpdate = nextJob.arrivalTime; if (currentJob == null) { // There is no job executing currently. // So, arrived job will go directly to the server instead of // the waiting queue. currentJob = nextJob; currentJob.waitingTime = 0; currentJobDepartureTime = currentJob.arrivalTime + currentJob.serviceTime; ttGraphThroughput.add(ttGraphThroughput.get(ttGraphThroughput.size() - 1)); ttGraphTime.add(currentJob.arrivalTime); } else { // Arrived job will go to the waiting queue. // Dropping packet when queue is full. if (waitingQueue.size() < maxQueueSize) { qltGraphQueueLength.add((double) waitingQueue.size()); qltGraphTime.add(nextJob.arrivalTime - epsilon); waitingQueue.add(nextJob); qltGraphQueueLength.add((double) waitingQueue.size()); qltGraphTime.add(nextJob.arrivalTime); } } if (nextJob.id == N - 1) { nextJob = jobWhichNeverArrives; } else { nextJob = job[nextJob.id + 1]; } } else if (nextJob.arrivalTime > currentJobDepartureTime) { // Next event is a departure. if (numberOfJobs == 0) { ttGraphThroughput.add(0.0); ttGraphTime.add(currentJobDepartureTime - epsilon); } numberOfJobs++; currentJob.jobStatus = status.SERVED; totalServiceTime += currentJob.serviceTime; ttGraphThroughput.add(numberOfJobs / totalServiceTime); ttGraphTime.add(currentJobDepartureTime); QiTi += (waitingQueue.size() * (currentJobDepartureTime - lastUpdate)); lastUpdate = currentJobDepartureTime; if (currentJob.id == N - 1 || (nextJob.arrivalTime == Integer.MAX_VALUE && waitingQueue.size() == 0)) { qltGraphQueueLength.add(0.0); qltGraphTime.add(currentJobDepartureTime); // Last job has departed. break; } if (waitingQueue.isEmpty()) { // Server will become idle. currentJob = null; currentJobDepartureTime = Integer.MAX_VALUE; } else { // Server gets the first job from the waiting queue. qltGraphQueueLength.add((double) waitingQueue.size()); qltGraphTime.add(currentJobDepartureTime - epsilon); currentJob = waitingQueue.remove(); qltGraphQueueLength.add((double) waitingQueue.size()); qltGraphTime.add(currentJobDepartureTime); currentJob.waitingTime = currentJobDepartureTime - currentJob.arrivalTime; currentJobDepartureTime += currentJob.serviceTime; } } else { // A job departs and another job arrives at the same time. numberOfJobs++; totalServiceTime += currentJob.serviceTime; ttGraphTime.add(currentJobDepartureTime); ttGraphThroughput.add(numberOfJobs / totalServiceTime); if (waitingQueue.isEmpty()) { currentJob = nextJob; currentJob.waitingTime = 0; currentJobDepartureTime = currentJob.arrivalTime + currentJob.serviceTime; } else { // Server gets the first job from the waiting queue. currentJob = waitingQueue.remove(); currentJob.waitingTime = currentJobDepartureTime - currentJob.arrivalTime; currentJobDepartureTime += currentJob.serviceTime; waitingQueue.add(nextJob); } if (nextJob.id == N - 1) { nextJob = jobWhichNeverArrives; } else { nextJob = job[nextJob.id + 1]; } } } double[] X = new double[qltGraphTime.size()]; double[] Y = new double[qltGraphTime.size()]; for (int i = 0; i < X.length; i++) { X[i] = qltGraphTime.get(i); Y[i] = qltGraphQueueLength.get(i); } DecimalFormat dformat = new DecimalFormat("#.####"); String qLength = (maxQueueSize == Integer.MAX_VALUE) ? "Infinite" : "" + maxQueueSize; String title = "Queue Length vs Time\n[Jobs: " + N + ", Mean Arrival Rate: " + lambda + ", Mean service rate: "+ mu + ", Maximum Queue Length: "+ qLength + ", Average Queue length = " + dformat.format(QiTi / currentJobDepartureTime); if(mu > lambda){ title = title + ", Theoretical Mean Queue Length: " + dformat.format((lambda * lambda) / (mu * (mu - lambda))); } title += " ]"; LineChart.createChart(X, Y, title, "Time", "QueueLength"); double[] X1 = new double[ttGraphTime.size()]; double[] Y1 = new double[ttGraphThroughput.size()]; for (int i = 0; i < X1.length; i++) { X1[i] = ttGraphTime.get(i); Y1[i] = ttGraphThroughput.get(i); } title = "Throughput vs Time\n[Jobs: " + N + ", Mean Arrival Rate: " + lambda + ", Mean service rate: "+ mu + ", Maximum Queue Length: "+ qLength + ", Final Throughput = " + dformat.format(numberOfJobs / totalServiceTime) + "]"; LineChart.createChart(X1, Y1, title, "Time", "Throughput"); /*for (int i = 0; i < N; i++) { if (job[i].jobStatus == status.SERVED) { out.write("Job ID = " + job[i].id + " Arrival Time = " + String.format("%.3f", job[i].arrivalTime) + " Service Time = " + String.format("%.3f", job[i].serviceTime) + " Waiting Time = " + String.format("%.3f", job[i].waitingTime) + " Response Time = " + String.format("%.3f", job[i].getResponseTime()) + "\n"); } else { out.write("Job ID = " + job[i].id + " Arrival Time = " + String.format("%.3f", job[i].arrivalTime) + " DROPPED" + "\n"); } }*/ out.write("Average Queue length = " + QiTi / currentJobDepartureTime + " Throughput = " + numberOfJobs / totalServiceTime + "\n"); out.flush(); out.close(); } static double nextRandomExponential(Random rnd, double l) { double expX = -1 * Math.log(1 - rnd.nextDouble()) / l; return expX; } }