So I have a problem I've been wracking my brain over for about a week now. The situation is:
Consider a checkout line at the grocery store. During any given
second, the probability that a new customer joins the line is 0.02 (no
more than one customer joins the line during any given second). The
checkout clerk takes a random amount of time between 20 seconds to 75
seconds to serve each customer. Write a program to simulate this
scenario for about ten million seconds and print out the average
number of seconds that a customer spends waiting in line before the
clerk begins to serve the customer. Note that since you do not know
the maximum number of customers that may be in line at any given time,
you should use an ArrayList and not an array.
The expected average wait time is supposed to be between 500 and 600 seconds. However, I have not gotten an answer anywhere close to this range. Given that the probability of a new customer in the line is only 2%, I would expect the line to never have more than 1 person in it, so the average wait time would be about 45-50 secs. I have asked a friend (who is a math major) what his view on this problem, and he agreed that 45 seconds is a reasonable average given the 2% probability. My code so far is:
package grocerystore;
import java.util.ArrayList;
import java.util.Random;
public class GroceryStore {
private static ArrayList<Integer> line = new ArrayList();
private static Random r = new Random();
public static void addCustomer() {
int timeToServe = r.nextInt(56) + 20;
line.add(timeToServe);
}
public static void removeCustomer() {
line.remove(0);
}
public static int sum(ArrayList<Integer> a) {
int sum = 0;
for (int i = 0; i < a.size(); i++) {
sum += a.get(i);
}
return sum;
}
public static void main(String[] args) {
int waitTime = 0;
int duration = 10000;
for (int i = 0; i < duration; i++) {
double newCust = r.nextDouble();
if (newCust < .02) {
addCustomer();
}
try {
for (int j = 0; j < line.get(0); j++) {
waitTime = waitTime + sum(line);
}
} catch (IndexOutOfBoundsException e) {}
if (line.isEmpty()) {}
else {
removeCustomer();
}
}
System.out.println(waitTime/duration);
}
}
Any advice about this would be appreciated.
Here's some pseudocode to help you plan it out
for each second that goes by:
generate probability
if probability <= 0.02
add customer
if wait time is 0
if line is not empty
remove customer
generate a new wait time
else
decrement wait time
There's actually a very easy implementation of single server queueing systems where you don't need an ArrayList or Queue to stash customers who are in line. It's based on a simple recurrence relation described below.
You need to know the inter-arrival times' distribution, i.e., the distribution of times between one arrival and the next. Yours was described in time-stepped fashion as a probability of 0.02 of having a new arrival in a given tick of the clock. That equates to a Geometric distribution with p = 0.02. You already know the service time distribution - Uniform(20,75).
With those two pieces of info, and a bit of thought, you can deduce that for any given customer the arrival time is the previous customer's arrival-time plus a (generated) interarrival time; this customer can begin being served at either their arrival-time or the departure-time of the prior customer, whichever comes later; and they finish up with the server and depart at their begin-service time plus a (generated) service-time. You'll need to initialize the arrival-time and departure time of an imaginary zeroth customer to kick-start the whole thing, but then it's a simple loop to calculate the recurrence.
Since this looks like homework I'm giving you an implementation in Ruby. If you don't know Ruby, think of this as pseudo-code. It should be very straightforward for you to translate to Java. I've left out details such as how to generate the distributions, but I have actually run the complete implementation of this, replacing the commented lines with statistical tallies, and it gives average wait times around 500.
interarrival_time = Geometric.new(p_value)
service_time = Uniform.new(service_min, service_max)
arrival_time = depart_time = 0.0 # initialize zeroth customer
loop do
arrival_time += interarrival_time.generate
break if arrival_time > 10_000_000
start_time = [arrival_time, depart_time].max
depart_time = start_time + service_time.generate
delay_in_queue = start_time - arrival_time
# do anything you want with the delay_in_queue value:
# print it, tally it for averaging, whatever...
end
Note that this approach skips over the large swathes of time where nothing is happening, so it's a quite efficient little program compared to time-stepping through every tick of the simulated clock and storing things in dynamically sized containers.
One final note - you may want to ignore the first few hundred or thousand observations due to initialization bias. Simulation models usually need a "warm-up" period to remove the effect of the programmatically necessary initialization of variables to arbitrary values.
Instead of using an ArrayList, a Queue might be better suited for managing the customers. Also, remove the try/catch clause and a throws IndexOutOfBoundsException to the main function definition.
Related
I'm working on a game and I want my enemies to spawn with a delay between 1-5 seconds. My code for that part looks like this:
#Override
public void run() {
try {
while (true) {
Random r = new Random();
int cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
long lastSpawn = 0;
long time = System.currentTimeMillis();
if (time > lastSpawn + cooldownTime) {
System.out.println(cooldownTime);
addEnemies();
lastSpawn = time;
}
If I understand nextInt correctly this should spawn enemies 1000-5000 ms apart every time, but my results are really weird and I can't quite figure out why. This is an example of what it would look like if I print cooldownTime.
2523
1190
1095
1061
1168
1119
1052
1159
1071
1076
1000
1394
1249
1070
And so on... It seems that the first enemy is truly spawned randomly and the others are always in the low 1000's. This happens every time. Does anyone know why it's like that? I'm quite lost.
Calling Random r = new Random(); repeatedly is extremely pathological and ruins the statistical properties of the generator. (The results you get are most likely a strong function of your system clock time.)
Do that step once and your results will be far better. (There are also more efficient ways to implement a delay than this - consider sleep - Java compilers are not yet optimising out burn loops.)
Note also that your observations are further complicated by the fact that you are not printing every number drawn.
Not sure if this your exact issue, but you had some problems with the logic of System.currentTimeMillis() and were just looking at the total current time and not the difference between the time.
Additionally you only want to look for a new random value once the if actually executes so you do not generate a Random number each loop iteration, but rather each time an enemy spawns you generate a new one for the next enemy.
Here is the modified code that takes all of this into account:
Random r = new Random();
long time = System.currentTimeMillis();
int cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
while (true) {
long timeDifference = System.currentTimeMillis() - time;
if (timeDifference > cooldownTime) {
//addEnemies();
System.out.println(timeDifference); //Prints the time taken for enemy to spawn
cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
time = System.currentTimeMillis(); //set the initial time again
}
}
This will generate a random number between 1000 and 5000 and execute the if block each time after the delay, resetting the values in the if to do it forever.
However, there are most likely better ways to add a delay to your logic like some comments pointed out(sleep), but this is the corrected logic for your method.
Well, first of all, several of your variables are being created INSIDE the loop. You need to move the creation of the Random object, as well as the lastSpawn and cooldown variables outside the loop. This is because the lastSpawn variable is being overwritten each time the loop executes with 0, meaning you're always checking if the current time is greater than 0. You need to store it outside the loop so that it will retain the last value you assigned to it. For the same reason, cooldown needs to be outside the loop because you're generating a new cooldown every loop, and System.currentTimeMillis() is ALWAYS going to be larger than it because System.currentTimeMillis() gets you the system time offset from January 1, 1970. Finally, as System.currentTimeMillis() is represented by a long, you'll want any time-related variables to be long as well, otherwise you could end up overflowing your variable if the current time in milliseconds is too high for an integer to store.
Here is a better way to achieve what you're looking to do:
import java.util.Random;
public class RandomCooldown {
public static void main(String [] args) {
Random rand = new Random();
long start = System.currentTimeMillis();
long lastSpawn = start;
long cooldown = getCooldown(rand);
while(true) {
long time = System.currentTimeMillis();
long elapsed = (time - lastSpawn);
if(elapsed >= cooldown) {
System.out.println("Adding enemies!");
cooldown = getCooldown(rand); // only generate a new cooldown once the old cooldown has been surpassed
lastSpawn = time;
}
}
}
public static long getCooldown(Random rand) {
return (long)((rand.nextInt(4000) + 1) + 1000);
}
}
Hope this helps!
Here is a suggestion that works as I think you like. I has not one but two loops. The outer loop generates new enemies and the inner loop makes updates (not sure how much of that this enemy generating thread needs to update but included it just in case).
public void run() {
Random r = new Random();
// setup
while (true) {
int wait = r.nextInt((5000 - 1000) + 1) + 1000;
long time = System.currentTimeMillis();
System.out.println("Adding enemies at " + time
+ ", next add roughly in " + wait + " ms.");
while (wait + time > System.currentTimeMillis()) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// update
}
}
}
The Random object is reused and the call Thread.sleep(30) makes the thread hand over control for 30 ms, a time during which the CPU can do more useful things than busy wait. (30 is BTW not a magic number that must be used but just a number I chose. You need to experiment and find what number works best in your game.)
The print out shows what happens.
Adding enemies at 1565096018610, next add roughly in 2890 ms.
Adding enemies at 1565096021530, next add roughly in 2301 ms.
Adding enemies at 1565096023863, next add roughly in 4944 ms.
Adding enemies at 1565096028813, next add roughly in 3042 ms.
Adding enemies at 1565096031879, next add roughly in 2661 ms.
... and so on. The actual numbers will not be the same of course when you run this code but similar.
Hope this helps and good luck with your game!
It looks fine to me. You should probably use the same Random() instance for each iteration. And remember: humans have no ability to perceive randomness. Alternatively, you could try seeding the Random-Generator (using the Random(long seed) constructor), just in case there's some weird stuff happening with your seed.
I'm new to java and I'm trying to constantly add "zombie" entity to my game after a delay, and then shortening the delay as time goes on.
In my PlayState class I've got the code to spawn the entities in a position when my update(); method is run (which houses getInput(); and such. Not shown for clarity.)
public void update(long elapsed) {
this.entities.add(new Zombie(-535));
}
How would i make a delay that shortens? I'm guessing I would make the delay and then use a multiplier which i have getting smaller every 10 seconds or so, but how would I do this?
Now, I don't know much about the finer workings of your class, but this might give you a general idea of what I mean:
int counter = 50;
for(int i = 100; i >= 0; i--)
{
if(i == counter)
{
counter = counter / 2;
this.entities.add(new Zombie(-535));
}
}
Suppose i is the the total run-time of the game, and counter represents a percent of the game when you want to add a zombie.
If you want to add a zombie after 50% of the run-time (here, 100 seconds), then as the time reduces, you check if the time has come to add a zombie (Here, 50 seconds).
What I've done here is reduce the delay to half, and continue checking if the time has come to add a zombie.
Maybe you could call sleep on your thread of execution:
int sleepInMs = 5000
Thread.sleep(sleepInMs);
sleepInMs+=1000; //Then of course handle the case when sleepInMs == 0
Really need more information about your implementation.
For a simple delay, use "delay ms;"
Edit ms for the number of milliseconds you want. 1000 milliseconds is one second
I have a java project to do and the requirements are:
create a loop that repeats the following experiment 10 times. After all 10 iterations are complete, print the average time for one iteration of the experiment:
Select a random number r from 0 to n.
Using the System class, note the start time of the experiment
Repeatedly multiply two 9-digit values in a loop for r iterations. You need not preserve
the result of this multiplication.
note the end time of the experiment
This is what I have so far:
package lee_lab02;
import java.util.Random;
import java.util.Scanner;
public class Benchmark {
public Benchmark() {
}
public static void main(String[] args) {
System.out.println("Please enter a value for n:");
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
long start = System.currentTimeMillis();
for(int i=1; i<=10; i++)
{
Random rand = new Random();
double r = rand.nextInt(n);
for(int z=1;z<r;z++)
{
long num1 = 145893123;
long num2 = 901234278;
long num3 = num1 * num2;
}
}
long end = System.currentTimeMillis();
long totTime = end - start;
long avg = totTime/10;
System.out.println(avg);
}
}
The output of this prompts me with "Please enter a value for n:" but the time is not recorded. What am I doing wrong here?
There's nothing wrong with your program. It is just that you are performing trivial operations in your for loop that don't take much time at all. All the iterations are completed in less than 1 millisecond. Just add Thread.sleep(500); in your for loop and you will see the average getting printed as you expect it.
Also, use double for avg. (double avg = totTime/10.0;).
To perform this kind of benchmarl test, you better need to use nanoTime() (remember that 1 000 000ns = 1ms (currentTimeMillis)), except if your programm takes more than 10sec in that case get the time in ms is sufficient (but still possible to use nanoTime()) :
long start = System.nanoTime();
int nbRound = 10;
for (int i = 1; i <= nbRound; i++) {
//...
}
long end = System.nanoTime();
long totTime = end - start;
long avg = totTime / nbRound;
System.out.println(avg);
I'va also added a variable nbRound because it's very quick to make a mistake and write different values in the loop and in the computation of the average, so better use variable when you can
And because you program does small things you need to set n to a high value, my test :
n=100 ==> 105 673ns
n=100000 ==> 1 720 963ns (1.7ms)
The scan.nextInt() method is a blocking method. So your current thread blocks and the code never moves ahead to execute the remaining lines of code.
You need to change your design a bit to handle this blocking of nextInt() method. You can call the nextInt() method in another thread and remaining logic in another and can control their processing so your average time calculation logic runs.
This is just a hypothetical question, but could be a way to get around an issue I have been having.
Imagine you want to be able to time a calculation function based not on the answer, but on the time it takes to calculating. So instead of finding out what a + b is, you wish to continue perform some calculation while time < x seconds.
Look at this pseudo code:
public static void performCalculationsForTime(int seconds)
{
// Get start time
int millisStart = System.currentTimeMillis();
// Perform calculation to find the 1000th digit of PI
// Check if the given amount of seconds have passed since millisStart
// If number of seconds have not passed, redo the 1000th PI digit calculation
// At this point the time has passed, return the function.
}
Now I know that I am horrible, despicable person for using precious CPU cycles to simple get time to pass, but what I am wondering is:
A) Is this possible and would JVM start complaining about non-responsiveness?
B) If it is possible, what calculations would be best to try to perform?
Update - Answer:
Based on the answers and comments, the answer seems to be that "Yes, this is possible. But only if it is not done in Android main UI thread, because the user's GUI will be become unresponsive and will throw an ANR after 5 seconds."
A) Is this possible and would JVM start complaining about non-responsiveness?
It is possible, and if you run it in the background, neither JVM nor Dalvik will complain.
B) If it is possible, what calculations would be best to try to perform?
If the objective is to just run any calculation for x seconds, just keep adding 1 to a sum until the required time has reached. Off the top of my head, something like:
public static void performCalculationsForTime(int seconds)
{
// Get start time
int secondsStart = System.currentTimeMillis()/1000;
int requiredEndTime = millisStart + seconds;
float sum = 0;
while(secondsStart != requiredEndTime) {
sum = sum + 0.1;
secondsStart = System.currentTimeMillis()/1000;
}
}
You can and JVM won't complain if your code is not part of some complex system that actually tracks thread execution time.
long startTime = System.currentTimeMillis();
while(System.currentTimeMillis() - startTime < 100000) {
// do something
}
Or even a for loop that checks time only every 1000 cycles.
for (int i = 0; ;i++) {
if (i % 1000 == 0 && System.currentTimeMillis() - startTime < 100000)
break;
// do something
}
As for your second question, the answer is probably calculating some value that can always be improved upon, like your PI digits example.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Okay. I am actually having an issue with logic more-so than with the actual coding of the main method.
Here is the background:
Your program will allow the manager to run multiple simulations for
different numbers of customers to determine the optimal number of
servers to hire. The exact service time for a customer is difficult to
predict. Using an average time based on past observations is one
possible approach. But, a more realistic strategy is to use average
minimum and maximum service times and generate each customer’s service
time randomly between these two numbers. The program will allow the
manager to set the minimum and maximum length of service time.
Predicting how often customers will arrive (represented usually by the
customer inter-arrival time) is another issue to be addressed. Again,
a strategy similar to generating service times is used. The program
will allow the manager to enter minimum and maximum inter-arrival
times. The exact time between arrivals for each customer will be
generated randomly between the two inter-arrival times.
Your program will read in the following data:
minimum and maximum service times (in minutes)
minimum and maximum inter-arrival times (in minutes)
the number of customers
Once the data items are entered, your program will display
the average wait time for the customers
the largest number of customers waiting in line during the entire simulation (maximum queue length).
In a situation like this, where the inputs variables are random in
nature, running a simulation just once is not adequate for drawing
reliable inferences. So, the program should also allow the manager to
run the simulation multiple times.
I wrote out pseudocode for the program and it seemed okay until I was working it by hand and realized that some of the times are actual times and some are durations of time.
The problem that I am having is that the only way I can calculate some things is by using info from a previous customer that is no longer available as that customer has been removed from the queue (adapted from a single linked list).
How can I organize the simulation by time instead of customer arrival? (Algorithm help would be preferred to code as it would most help me understand the logic, which is the problem I am having.)
Here is the code that I have so far in the main method:
/**
* Class FastFoodSimulation simulates customer service at a fast food restaurant.
* This simulation serves as a tool for managers to determine how many servers are needed
* for average amount of customers to be serviced in a timely manner.
*
* #author (Ebony Brewer)
* #version (10272013)
*/
import javax.swing.JOptionPane;
import java.util.Random;
public class FastFoodSimulation
{
public static void main(String args[])
{
int time;
boolean reRun = true;
int maxQueueLength;
int customerCounter;
int serviceTime;
int arrivalTime;
int departureTime;
int waitTime;
int interArrivalTime;
int totalTime;
int totalWaitTime;
do
{
int minServiceTime = Integer.parseInt(JOptionPane.showInputDialog("Enter Minimum Service Time (in minutes)."));
int maxServiceTime = Integer.parseInt(JOptionPane.showInputDialog("Enter Maximum Service Time (in minutes)."));
int minInterArrivalTime = Integer.parseInt(JOptionPane.showInputDialog("Enter Minimum Time Between Customers(in minutes)."));
int maxInterArrivalTime = Integer.parseInt(JOptionPane.showInputDialog("Enter Maximum Time Between Customers(in minutes)."));
int numberOfCustomers = Integer.parseInt(JOptionPane.showInputDialog("Enter Number of Customers to Simulate."));
time = 0;
//LinkedQueue queue = new LinkedQueue();
maxQueueLength = 0;
customerCounter = 0;
arrivalTime = 0;
departureTime = 0;
waitTime = 0;
totalTime = 0;
totalWaitTime = 0;
while(customerCounter < numberOfCustomers)
{
serviceTime = randInt(minServiceTime, maxServiceTime);
interArrivalTime = randInt(minInterArrivalTime, maxInterArrivalTime);
arrivalTime = time + interArrivalTime;
SimulationCustomer cust = new SimulationCustomer();
cust.setServiceTime(serviceTime);
cust.setInterArrivalTime(interArrivalTime);
cust.setArrivalTime(arrivalTime);
if(time == cust.getArrivalTime() && maxQueueLength == 0)
{
//queue.offer(cust);
customerCounter++;
departureTime = arrivalTime + serviceTime;
totalTime = departureTime - arrivalTime;
cust.setWaitTime(0);
cust.setDepartureTime(departureTime);
cust.setTotalTime(totalTime);
//int length = queue.size();
//if(length > maxQueueLength)
// maxQueueLength = length;
//queue.remove(cust);
time++;
}
else if(time == cust.getArrivalTime() && maxQueueLength >= 1)
{
//queue.offer(cust);
customerCounter++;
}
}
}
while(reRun);
}
/**
* Returns a psuedo-random number between min and max, inclusive.
* The difference between min and max can be at most
* Integer.MAX_VALUE - 1
* By Greg Case # http://stackoverflow.com/questions/363681/generating-random-numbers-in-a-range-with-java
*
* #param min Minimim value
* #param max Maximim value. Must be greater than min.
* #return Integer between min and max, inclusive.
* #see java.util.Random#nextInt(int)
*/
public static int randInt(int min, int max)
{
// Usually this can be a field rather than a method variable
Random rand = new Random();
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
}
You don't have a complete/workable logical structure for the simulation.
At the moment, you drive the simulation by customers walking in the door. Having just set their arrival time, you then check the arrival time (redundantly).. before trying to decide what to do with them.
Driving the loop by customers walking in the door is not necessarily wrong -- but it's not necessarily simplest or easiest either. You have to drive the loop by something, but could drive by time instead.
You need to manage "state" within the restaurant. Currently you don't have logic to put customers into the queue, to finish serving customers, or to serve the next customers in the queue.
"Customer states" within the restaurant define the transitions, aka events, which your program must implement & model for this simulation to be correct.
As to your comments: all the generated & specified times are intervals. You may internally want to use or generate a reference time or timestamp for scheduling upcoming events, or to print for logging purposes.
There's no problem with needing info from the last customer after using it -- you can either keep a last variable & use that later, or randomly generate nextCustomerTime while you still have the customer. This is all pretty simple.
Lastly, put your "single simulation run" code into a method, or (perhaps even better) a separate class. Simpler & clearer will enable you to more clearly isolate your relevant logic, work on it & focus on getting it right.
You should use a PriorityQueue<FastFoodEvent> instance, where the events include some of CustomerEnters, CustomerOrders, CustomerServed. The events all have a time variable, and the queue will order by time.
You'll want a Customer class that has an id and some I/O methods.
You'll want some Cashier objects, each of which will try to handle customers from the queue. While a cashier is handling a customer, he does not handle another. Alternatively, he may have his own queue.