org.cloudsimplus.distributions.PoissonDistr Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloudsimplus Show documentation
Show all versions of cloudsimplus Show documentation
CloudSim Plus: A modern, highly extensible and easier-to-use Java 17+ Framework for Modeling and Simulation of Cloud Computing Infrastructures and Services
/*
* CloudSim Plus: A modern, highly-extensible and easier-to-use Framework for
* Modeling and Simulation of Cloud Computing Infrastructures and Services.
* http://cloudsimplus.org
*
* Copyright (C) 2015-2021 Universidade da Beira Interior (UBI, Portugal) and
* the Instituto Federal de Educação Ciência e Tecnologia do Tocantins (IFTO, Brazil).
*
* This file is part of CloudSim Plus.
*
* CloudSim Plus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CloudSim Plus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CloudSim Plus. If not, see .
*/
package org.cloudsimplus.distributions;
import org.apache.commons.math3.util.CombinatoricsUtils;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.IntStream;
/**
* A Pseudo-Random Number Generator which returns numbers
* following a Poisson Distribution, modeling the probability of an event
* to happen a number of times in a given time interval.
*
* @author Manoel Campos da Silva Filho
* @see Poisson Distribution
* @since CloudSim Plus 1.2.0
* TODO There is the {@link org.apache.commons.math3.distribution.PoissonDistribution}
* implementation already.
* This class should simply extend it, as the other class on this package do.
*/
public class PoissonDistr implements DiscreteDistribution {
/** @see #isApplyAntitheticVariates() */
private boolean applyAntitheticVariates;
/**
* A Uniform Pseudo-Random Number Generator used internally.
*/
private final UniformDistr rand;
/** @see #getLambda() */
private double lambda;
/** @see #getK() */
private int k;
/**
* Creates a Poisson Pseudo-Random Number Generator to check the probability
* of 1 event ({@link #getK() k = 1}) to happen at each time interval.
*
* @param lambda the average number of events that happen at each 1 time unit.
* If one considers the unit as minute, this value means the average number of arrivals
* at each minute.
* @param seed the seed to initialize the internal uniform Pseudo-Random Number Generator
* @see #setK(int)
* @see #setLambda(double)
*/
public PoissonDistr(final double lambda, final long seed) {
if (seed < 0) {
throw new IllegalArgumentException("Seed cannot be negative");
}
this.rand = new UniformDistr(seed);
this.k = 1;
this.setLambda(lambda);
}
/**
* Creates a Poisson Pseudo-Random Number Generator to check the probability
* of 1 event ({@link #getK() k = 1}) to happen at each time interval.
*
* @param lambda average number of events by interval.
* For instance, if it was defined 1 event to be expected at
* each 2.5 minutes, it means that 0.4 event is expected
* at each minute (1/2.5).
* @see #setK(int)
*/
public PoissonDistr(final double lambda) {
this(lambda, StatisticalDistribution.defaultSeed());
}
/**
* Gets the average number of events (λ) that are expected to happen at each 1 time unit.
* It is the expected number of events to happen each time,
* also called the event rate or rate parameter.
*
* If one considers the unit as minute, this value means the average number of arrivals
* at each minute. It's the inverse of the {@link #getInterArrivalMeanTime()}.
*
* @return
*/
public double getLambda() {
return lambda;
}
/**
* Sets the average number of events (λ) that are expected to happen at each 1 time unit.
* It is the expected number of events to happen each time,
* also called the event rate or rate parameter.
*
* If one considers the unit as minute, this value means the average number of arrivals
* at each minute. It's the inverse of the {@link #getInterArrivalMeanTime()}.
*
* @param lambda the value to set
*/
private void setLambda(final double lambda) {
this.lambda = lambda;
}
/**
* Gets the probability to arrive {@link #getK() k} events in the current time,
* considering the mean arrival time {@link #getLambda() lambda (λ)},
* which is represented as {@code Pr(k events in time period)}.
* It computes the Probability Mass Function (PMF) of the Poisson distribution.
*
* @return the probability of a {@link #sample() random variable} to be equal to k
* @see Poisson Probability Mass Function
*/
public double eventsArrivalProbability() {
return (Math.pow(getLambda(), k) * Math.exp(-getLambda())) / CombinatoricsUtils.factorial(k);
}
/**
* Checks if at the current time, {@link #getK() k} events have happened,
* considering the {@link #eventsArrivalProbability() probability of these k events}
* to happen in a time interval.
*
* @return true if k events have happened at the current time, false otherwise
*/
public boolean eventsHappened() {
return rand.sample() <= eventsArrivalProbability();
}
/**
* Gets a random number that represents the next time (from current time or last generated event)
* that an event will happen, considering events arrival rate defined
* by {@link #getLambda() lambda (λ)}.
* The time unit (if seconds, minutes, hours, etc) is the same
* considered when setting a value to the {@link #getLambda() lambda} attribute.
*
*
* Calling this method for the first time returns the next event arrival time.
* The retuning values for consecutive calls can be dealt in one of the following ways:
*
* -
* If you are generating all random event arrivals at the beginning of the simulation,
* you need to add the previous time to the next event arrival time.
* This way, the arrival time of the previous event is added to the next one.
* For instance, if consecutive calls to this method return the values 60 and 25,
* from the current time, that means:
* (i) the first event will arrive in 60 seconds;
* (ii) the next event will arrive in 85 seconds, that is 25 seconds after the first one.
*
* -
* If you are generating event arrivals during simulation runtime,
* you must NOT add the previous time to the generated event time,
* just use the returned value as the event arrival time.
*
*
*
*
* Poisson inter-arrival times are independent and identically distributed
* exponential random variables with mean 1/λ.
*
* @return
* @see Monte Carlo Methods and Models in Finance and Insurance.
* Ralf Korn, Elke Korn, et al. 1st edition, 2010. Section 2.4.1: Exponential distribution. Page 33.
* @see Related distributions
*/
@Override
public double sample() {
return -Math.log(1.0 - rand.sample()) / getLambda();
}
@Override
public long getSeed() {
return rand.getSeed();
}
@Override
public boolean isApplyAntitheticVariates() {
return applyAntitheticVariates;
}
@Override
public PoissonDistr setApplyAntitheticVariates(final boolean applyAntitheticVariates) {
this.applyAntitheticVariates = applyAntitheticVariates;
return this;
}
@Override
public double originalSample() {
return rand.sample();
}
/**
* Gets the number of events to check the probability for them to happen
* in a time interval (default 1).
*
* @return
*/
public int getK() {
return k;
}
/**
* Sets the number of events to check the probability to happen
* in a time interval.
*
* @param k the value to set
*/
public void setK(final int k) {
this.k = k;
}
/**
* Gets the mean time between arrival of two events,
* which is the inverse of {@link #getLambda() lambda (λ)}.
* The time unit (if seconds, minutes, hours, etc.) is the same
* considered when setting a value to the {@link #getLambda() lambda} attribute.
*
* @return
*/
public double getInterArrivalMeanTime() {
return 1.0 / lambda;
}
/**
* Tests the simulations of customers arrivals in a Poisson process.
* All the code inside this method is just to try the class.
* That is way it declares internal methods as Functional
* objects, instead of declaring such methods
* at the class level and just calling them.
*
* @param args
*/
public static void main(final String[] args) {
//@TODO This method should be moved to a meaningful example class that creates Cloudlets instead of customers
/*
* Mean number of customers that arrives per minute.
* The value of 0.4 customers per minute means that 1 customer will arrive at every 2.5 minutes
* (i.e. 1 minute / 0.4 customer per minute = 1 customer at every 2.5 minutes).
* This is the inter-arrival time (in average).
*
* The time unit is irrelevant for the generator.
* If you are considering minutes, you just have to ensure your simulation
* clock is in minutes too.
*/
final double MEAN_CUSTOMERS_ARRIVAL_MINUTE = 0.4;
/*
* Time length of each simulation in minutes.
*/
final int SIMULATION_TIME_LENGTH = 25;
//If the arrival of each customer must be shown.
final boolean showCustomerArrivals = true;
/*
* Number of simulations to run.
*/
final int NUMBER_OF_SIMULATIONS = 100;
final BiConsumer printArrivals = (poisson, minute) -> {
if (showCustomerArrivals) {
System.out.printf("%d customers arrived at minute %d%n", poisson.getK(), minute);
}
};
/**
* A {@link Function} to simulate the arrival of customers for a given time period.
* This is just a method to test the implementation.
*
* @param poisson the PoissonDistr object that will compute the customer arrivals probabilities
* @return the number of arrived customers
*/
final Function runSimulation = poisson -> {
/*We want to check the probability of 1 customer (k) to arrive at each
single minute. The default k value is 1, so we don't need to set it.*/
final int totalArrivedCustomers =
IntStream.range(0, SIMULATION_TIME_LENGTH)
.filter(time -> poisson.eventsHappened())
.peek(time -> printArrivals.accept(poisson, time))
.map(time -> poisson.getK())
.sum();
System.out.printf(
"\t%d customers arrived in %d minutes%n", totalArrivedCustomers, SIMULATION_TIME_LENGTH);
System.out.printf(
"\tArrival rate: %.2f customers per minute. Customers inter-arrival time: %.2f minutes in average%n",
poisson.getLambda(), poisson.getInterArrivalMeanTime());
return totalArrivedCustomers;
};
double customersInAllSimulations = 0;
PoissonDistr poisson = null;
final long seed = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_SIMULATIONS; i++) {
poisson = new PoissonDistr(MEAN_CUSTOMERS_ARRIVAL_MINUTE, seed + i);
System.out.printf("Simulation number %d%n", i);
customersInAllSimulations += runSimulation.apply(poisson);
}
final double mean = customersInAllSimulations / NUMBER_OF_SIMULATIONS;
System.out.printf("%nArrived customers average after %d simulations: %.2f%n",
NUMBER_OF_SIMULATIONS, mean);
System.out.printf(
"%.2f customers expected by each %d minutes of simulation with inter-arrival time of %.2f minutes%n",
poisson.getLambda() * SIMULATION_TIME_LENGTH, SIMULATION_TIME_LENGTH,
poisson.getInterArrivalMeanTime());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy