模拟:
MD1Queue 是 Queue 客户端 ,你可能使用它来验证这类数学结果。他是基于事件模拟的简单示例:我们产生的事件发生在特定时间,为了事件相应的调整我们的数据结构,模拟当事件出现时,发生了什么。在M/D/1队列中,有两类事件:不是有顾客到达,就是提供顾客服务,因此我们需要维护两个变量:
· nextService 是下一个服务的时间
· nextArrival 是下一个到达的时间
为了模拟到达事件,在队列中插入double值,nextvalArrival是到达时间;为了模拟服务,中队列中删除值,计算等待时间(离开队列时间减去服务时间nextService是到达时间)并将等待时间数据点加到直方图上(直方图程序Histogram)。在多次试验之后,结果的形状是M/D/1排队系统的特性。从实用观点来看,这个过程中的一个最重要特征是,通过使用不同的λ和μ值运行MD1Queue,你会发现当服务率逼近到达率时,平均等待时间和队列长度可能剧增。当服务费率很高时,直方图有一个可见的尾部,此时随着等待时间的减少,顾客需要等待时间的频率减少到几乎可以忽略不计。但是,当服务费率逼近到达率时,直方图的尾部在最多值处伸展,因此至少能显示顾客最长等待时间频率。
/*************************************************************************
* Compilation: javac MD1Queue.java
* Execution: java MD1Queue lambda mu
* Dependencies: Queue.java
*
* % java MD1Queue .167 .25
*
* % java MD1Queue .167 .22
*
*************************************************************************/
public class MD1Queue {
public static void main(String[] args) {
double lambda = Double.parseDouble(args[0]); // arrival rate
double mu = Double.parseDouble(args[1]); // service rate
Histogram hist = new Histogram(60 + 1);
Queue<Double> queue = new Queue<Double>();
StdDraw.setCanvasSize(700, 500);
double nextArrival = StdRandom.exp(lambda); // time of next arrival
double nextService = nextArrival + 1/mu; // time of next completed service
while (true) {
// next event is an arrival
while (nextArrival < nextService) {
queue.enqueue(nextArrival);
nextArrival += StdRandom.exp(lambda);
}
// next event is a service completion
double arrival = queue.dequeue();
double wait = nextService - arrival;
// update the histogram
StdDraw.clear();
hist.addDataPoint(Math.min(60, (int) (Math.round(wait))));
hist.draw();
StdDraw.show(20);
// update the queue
if (queue.isEmpty()) nextService = nextArrival + 1/mu;
else nextService = nextService + 1/mu;
}
}
}
public class Histogram {
private final double[] freq; // freq[i] = # occurences of value i
private double max; // max frequency of any value
public Histogram(int N) {
freq = new double[N];
}
// Add one occurrence of the value i.
public void addDataPoint(int i) {
freq[i]++;
if (freq[i] > max) max = freq[i];
}
// draw (and scale) the histogram.
public void draw() {
StdDraw.setYscale(0, max);
StdStats.plotBars(freq);
}
public static void main(String[] args) {
int N = Integer.parseInt(args[0]); // number of coins
int T = Integer.parseInt(args[1]); // number of trials
// create the histogram
Histogram histogram = new Histogram(N+1);
for (int t = 0; t < T; t++) {
histogram.addDataPoint(Bernoulli.binomial(N));
}
// display using standard draw
StdDraw.setCanvasSize(500, 100);
histogram.draw();
}
}
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Queue<Item> implements Iterable<Item>
{
private int N; // number of elements on queue
private Node first; // beginning of queue
private Node last; // end of queue
private class Node
{
private Item item;
private Node next;
}
public Queue()
{
first = null;
last = null;
}
public boolean isEmpty()
{
return first == null;
}
public int length()
{
return N;
}
public void enqueue(Item item)
{
Node x = new Node();
x.item = item;
if (isEmpty())
{
first = x;
last = x;
}
else
{
last.next = x;
last = x;
}
N++;
}
// remove and return the least recently added item
public Item dequeue()
{
if (isEmpty())
throw new RuntimeException("Queue underflow");
Item item = first.item;
first = first.next;
N--;
return item;
}
public Iterator<Item> iterator()
{
return new QueueIterator();
}
// an iterator, doesn't implement remove() since it's optional
private class QueueIterator implements Iterator<Item>
{
private Node current = first;
public boolean hasNext()
{
return current != null;
}
public void remove()
{
throw new UnsupportedOperationException();
}
public Item next()
{
if (!hasNext())
throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
像我们在许多其他应用中所分析的一样,使用模拟来验证还不完全理解的数学模型试验就更个复杂情况的一个起点。在队列中的实际应用中,我们可以有多个队列,多台服务器,多级服务器,限制队列大小和许多其他限制。而且,到达分布和服务时间可能不能用数学方式描述。在这写情况下,我们不可能求助于其他,只能使用模拟。我们可以更多的使用模拟来调整系统设计参数(比如:服务率)来适当反映外部的环境(比如:到达率)。
2011年7月7日02:30稿。