SS 2006 Arbeitsblatt 4 / S. 1 von 9 9. Eine einfache Warteschlangen-Simulation. A) Allgemeine Bemerkungen. Die Warteschlange aus 8., wie auch solche mit nur endlich grossem Warteraum, können auf einfache Art simuliert werden, wenn man lediglich in Abhängigkeit von den Ankunfts- und Bedienungsraten eine Aussage über die Länge der Schlange zu verschiedenen Zeiten und den Erwartungswert ihrer Länge während der Dauer der Simulation gewinnen will. Zur Simulation auf einem Digitalrechner muss der zeit-kontinuierliche Ankunfts- und Bedienungsprozess zunächst diskretisiert werden. Als Zeitpunkte der jeweiligen Beobachtungen können feste Zeiten, die Zeiten der Ankünfte oder die der Weggänge aus dem System gewählt werden. Der folgende prinzipielle Algorithmus betrachtet das System gewissermassen in Momentaufnahmen zu festen äquidistanten Zeiten; die Zeitskalierung wird so gewählt, dass die Zeit in jedem Schritt um 1 zunimmt. Entsprechend sind die Raten λ und µ der beteiligten Prozesse und natürlich die Zeit T der Simulationsdauer anzupassen. B) Struktur des Algorithmus. Die zufälligen Zeiten für die Ankunft und die Bedienung der Klienten werden durch eine externe Routine erzeugt, in der die exponentiell verteilten Zeiten aus zufällig erzeugten Zahlen zwischen 0 und 1 berechnet und ausgegeben werden. Im Hauptprogramm wird zur Zeit t = 0 zunächst die Initialisierung vorgenommen. Unter den hier gewählten Anfangsbedingungen wird ein erster Zugang erzeugt und davor keine Bedienung angenommen. Die Zeitschleife beginnt dann mit t = 1. Bilanziert werden die Ankünfte im vorangegangenen Zeitintervall sowie die in dieser Zeit abgeschlossenen oder begonnenen Bedienungen (und damit die aktuelle Länge der Schlange). Bei jeder Ankunft wird der Zähler, der die Anzahl der Klienten in der Schlange angibt, um eins hochgesetzt (so lange dies bei endlich grossem Warteraum möglich ist), beim Abschluss jeder Bedienung um eins herabgesetzt, so lange die Schlange nicht leer ist. Ist die Schlange leer und noch ein Klient in der Bedienungsphase, dann wird dieser weiter bedient o- der falls das Ende der Bedienzeit noch innerhalb des Intervalls liegt aus dem System entlassen und die Bedienung so lange eingestellt, bis die Schlange nicht mehr leer ist. Bei jeder Momentaufnahme können daher die folgenden Situationen vorliegen (n ist die aktuelle Anzahl der Klienten in der Schlange): 1. n = 0 und keine Bedienung,
SS 2006 Arbeitsblatt 4 / S. 2 von 9 2. n > 0 und keine Bedienung (es ist nach einer Totzeit gerade ein Klient eingetroffen), 3. n = 0 und ein (der aktuell letzte) Klient wird bedient, 4. n > 0 und ein Klient wird bedient. Am Ende des Zeitintervalls wird die Zahl der Wartenden ausgegeben, die Zeit um eins weiter gezählt und die Schleife neu betreten, so lange die Zeit kleiner als die Simulationszeit ist. Ist die Simulationszeit erreicht, wird das Programm beendet. C) Formulierung des Algorithmus 9.1: Besonders einfach ist der Fall zu simulieren, in dem das System nur zwei mögliche Zustände besitzt: den Zustand 0, in dem es leer ist, und den Zustand 1, in dem eine Bedienung stattfindet (und weitere Ankünfte abgewiesen werden). Als globale Konstanten sind zu wählen T sim = Dauer eines Simulationslaufs und R = Anzahl der Durchläufe. 1. Initialisierung des Versuchs: Setze k = 1. 2. Initialisierung des Durchlaufs: Setze t = 0 und n = 0, λ = Ankunftsrate, µ = Bedienungsrate in Einheiten des Zeitschritts, T A = T B = Exporand(λ), Flag = Keine Bedienung. 3. Eintritt in die Zeitschleife: Setze t = t+1. Falls t T sim, gehe zu 6. 4. Falls T B t, setze Flag = Keine Bedienung und n = 0. 5. Falls T A t, prüfe, ob Flag = Keine Bedienung. Falls ja, setze T B = T A + Exporand(µ), n = 1 und Flag = Bedienung. Setze T A = T A + Exporand(λ). Gehe zu 4. 6. Falls k = R, beende das Programm, andernfalls setze k = k+1 und gehe zu 3. Hier eine Implementierung von Algorithmus 9.1 in C++ (die Ergebnisse werden in der Textdatei <wstest1.txt> ausgedruckt):
SS 2006 Arbeitsblatt 4 / S. 3 von 9 * Simulation einer (M/M/1)-Warteschlange mit 2 Zuständen *******************************************************************************/ #include <fstream.h> #include <stdlib.h> #include <math.h> #include <time.h> const R = 50; // Anzahl der Versuche const T = 500; // Dauer der Simulation // Hilfsprogramm: Erzeugung von exponentiell verteilten Zufallszahlen für Ankunft und Abgang double exporand(const double kappa) double nu, t; nu = kappa; t = (double) rand() / RAND_MAX; t = - log(t) / nu; return t; // Hauptprogramm: Regelmässiger Check in Zeitschritten von delta(t) = 1 int main() int n, t; double ankunftszeit, serverstartzeit, serverendzeit, chi, psi; double lambda(0.8), mu(0.9), p[2], rho(lambda/mu); bool no_service(true); srand((unsigned) time(null)); // Variation der Zufallszahlen nach // Systemzeit des Rechners ofstream out("wstest1.txt"); // Ausgabe in Textdatei "wstest1.txt" out << "Parameter des Systems: Ankünfte Lambda = "; out << lambda << ", Abgänge Mu = " << mu << ", Simulationsdauer: "; out << T << endl << endl; out << "Start mit p(0) = 1, p(1) = 0" << endl << endl; for (int r=0; r<r; r++) // Initialisierung n = 0; // Zu Beginn kein Wartender in der Schlange p[0] =1; p[1] = 0; ankunftszeit = 0; serverstartzeit = ankunftszeit; serverendzeit = 0; no_service = true; t = 1; // Erster Check bei t = 1 out.setf(ios::fixed); // Formatierung der Ausgabe
SS 2006 Arbeitsblatt 4 / S. 4 von 9 out.precision(2); // Check der Zeiten out << "Durchlauf " << r+1 << ": " << endl; out << "...Erste Ankunft: " << ankunftszeit << endl; // Verlauf der Simulation while (t < T) // Zeitschleife while (ankunftszeit <= t) // Ankünfte zwischen t-1 und t if ((ankunftszeit >= serverendzeit) (ankunftszeit == 0)) psi = exporand(mu); serverendzeit = ankunftszeit + psi; out << "...Ankunft um " << ankunftszeit << ", "; out << "Bedienung erfolgt - ist beendet um "; out << serverendzeit << endl; n = 1; no_service = false; out << "...Ankunft um " << ankunftszeit << ", "; out << "Klient abgewiesen" << endl; n = 0; chi = exporand(lambda); ankunftszeit = ankunftszeit + chi; // Buchhaltung if (n == 0) p[0]++; p[1]++; t++; out << "Status nach Durchlauf " << r+1 << ": " << endl; out << "p(0) = " << p[0] / T; out << ", stationär theor.: " << 1 / (1+rho) << endl; out << "p(1) = " << p[1] / T; out << ", stationär theor.: " << rho / (1+rho) << endl << endl; out.close(); return 0;
SS 2006 Arbeitsblatt 4 / S. 5 von 9 Bei genügend grosser Dauer eines Simulationslaufes im Vergleich zu den Ankunftsund Bedienungsraten und genügend grosser Anzahl der Versuche stimmen die Wahrscheinlichkeiten dafür, dass das System sich in einem der beiden Zustände befindet, gut mit den theoretischen Werten für die stationäre Lösung überein. D) Formulierung des Algorithmus 9.2: Etwas aufwendiger wird der Algorithmus, wenn der Warteraum nicht beschränkt ist: T sim = Dauer eines Simulationslaufs und R = Anzahl der Durchläufe. 3. Initialisierung: Setze t = 0 und n = 0, λ = Ankunftsrate, µ = Bedienungsrate in Einheiten des Zeitschritts, T A = Exporand(λ), Flag: Keine Bedienung. 4. Eintritt in die Zeitschleife Prüfe, ob t > T sim. Falls ja, beende das Programm, andernfalls gehe zu 3. 5. Prüfe, ob T A < t. Falls ja: Setze T A = T A + Exporand(λ), Setze n = n+1, Gehe zu 3. 6. Prüfe, ob der Server bedient. a. Falls nein: Prüfe, ob n = 0. Falls ja, setze T B = t. Falls nein, beginne einen neuen Bedienungsvorgang. b. Falls ja: Prüfe, ob T B < t. Falls ja: Setze T B = T B + Exporand(µ), Setze n = n-1, Falls n > 0, gehe zu 4 b. Andernfalls gehe zu 4. 7. Setze t = t + 1. Gib n aus und berechne den Anteil von n zum Erwartungswert der Anzahl der Klienten in der Schlange. Gehe zu 2. Ein Beispiel für eine Implementierung von Algorithmus 9.2 in C++ ist im folgenden angegeben. D) Implementierung von Algorithmus 9.2 in C++.
SS 2006 Arbeitsblatt 4 / S. 6 von 9 Die Implementierung kann aufgrund der einfachen Struktur des Algorithmus unmittelbar in einer Quelldatei WS01.cpp erfolgen. * Simulation einer (M/M/1)-Warteschlange Quelldatei WS01.cpp *******************************************************************************/ #include <fstream.h> #include <stdlib.h> #include <math.h> #include <time.h> // Zur Ausgabe in eine Textdatei // Standard-Library // Zur Berechnung von Zufallszahlen // Zur Variation der Zufallsausgaben const T = 500; // Dauer der Simulation // Hilfsprogramm: Erzeugung von exponentiell verteilten Zufallszahlen für // Ankunft und Abgang double exporand(const double kappa) double nu, t; nu = kappa; t = (double) rand() / RAND_MAX; t = - log(t) / nu; return t; // Hauptprogramm: Regelmässiger Check in Zeitschritten von delta(t) = 1 int main() int n, t; double azeit, bzeit, chi, psi, lambda(0.8), mu(0.9), N(0); bool no_service(true); // Bedienungsflag ofstream out("wstest1.txt"); out.precision(2); // Ausgabedatei out << "Parameter des Systems: Ankünfte Lambda = "; out << lambda << ", Abgänge Mu = " << mu << endl << endl;
SS 2006 Arbeitsblatt 4 / S. 7 von 9 srand((unsigned) time(null)); // Variation der Zufallszahlen nach System- // zeit des Rechners // Initialisierung n = 0; azeit = exporand(lambda); if (azeit < 1) n++; bzeit = 0; t = 1; out.setf(ios::fixed); out.precision(2); // Verlauf der Simulation while (t < T) while (azeit <= t) chi = exporand(lambda); n++; * Übersicht über die Ankunftszeiten - auskommentiert */ // out << "Ankunft vor: " << t << " : bei: " << azeit << endl; /******************************************************************************/ azeit = azeit + chi; if (no_service) if (n == 0) bzeit = t;
SS 2006 Arbeitsblatt 4 / S. 8 von 9 * Übersicht über die Bedienungszeiten - auskommentiert */ // out << "Weggang vor: " << t << " : bei: " << bzeit << endl; /******************************************************************************/ psi = exporand(mu); n--; * Übersicht über die Bedienungszeiten - auskommentiert */ // out << "Weggang vor: " << t << " : bei: " << bzeit << endl; /******************************************************************************/ bzeit = t + psi; no_service = false; while (bzeit < t) if (n>0) psi = exporand(mu); n--; * Übersicht über die Bedienungszeiten - auskommentiert */ // out << "Weggang vor : " << t << " : bei: " << bzeit << endl; /******************************************************************************/ bzeit = bzeit + psi; no_service = true; bzeit = t;
SS 2006 Arbeitsblatt 4 / S. 9 von 9 // Ausgabe der Länge der Schlange nach jedem Zeitschritt out << "Länge der Warteschlange zur Zeit " << t << " : " << n <<" "; for (int j=0; j<n; j++) out <<"*"; out << endl; t++; N = N + n; // Berechnung des Erwartungswertes der Schlangenlänge N = N / T; out << endl << "Mittlere Länge der Schlange: " << N << endl << endl; out.close(); return 0;