В первом примере моделируется обслуживание в бакалейной лав-
ке. Предположим, что лавка открыта 10 часов в день с пиковыми ча-
сами с 12 до 13 и с 17 до 18 часов. Период с 12 до 13 часов имеет
нагрузку в два раза, а с 17 до 18 - в три раза больше обычной.
При моделировании один генератор "порождает" покупателей, второй
генератор определяет время обслуживания покупателя, а третий ре-
шает, в какую очередь пойдет покупатель. Цель моделирования сос-
тоит в том, чтобы помочь управляющему найти оптимальное число
очередей, которые должны работать в обычный день при условии, что
число людей в очереди в любое время не превышало бы 10 и кассиры
не ожидали бы покупателей.
Ключ к данному типу моделирования состоит в создании многих
процессов. Хотя Турбо Паскаль непосредственно не поддерживает па-
раллельности, вы можете моделировать с помощью множества процес-
сов или с помощью главной программы с циклами. Далее показана
программа с ее глобальными данными для моделирования очередей без
поддержки процедур и функций:
var
gueues, count: array[0..9] of integer;
open: array[0..9] of boolean;
cust, time: integer;
a1, a2: integer;
y, x: integer;
change: boolean;
GraphDiver, GraphMode: integer;
begin
{переключение на графику, используя режим 4 CGA/EGA}
GraphDriver := CGA;
GraphMode := CGAC1;
InitGraph(GraphDriver, GraphMode, '');
SetColor(2);
SetLineStyle(SolidLn, 0, NormWidth);
a1:=1; a2:=203; {инициализация переменных генератора
случайных чисел}
change := FALSE;
cust := 0;
time := 0;
for x:=0 to 9 do begin
gueues[x]:=0; {инициализация очереди }
open[x]:=FALSE; { нет покупателей или очередей в
начале дня }
count[x]:=0; {счетчик очереди }
end;
OutTextXy(155, 190, '1 10');
OutTextXy(8,190,'Check-out lines: ');
{ теперь начинается день открытием первой очереди }
open[0] := TRUE;
repeat
AddCust;
AddQueue;
Display;
CheckOut;
Display;
if (time>30) and (time<50) then AddCust;
if (time>70) and (nime<80) then begin
AddCust;
AddCust;
end;
time := time+1;
until time>100; { конец дня }
ReadLn;
RestoreCrtMode;
end.
Элемент Graph.P включен, чтобы программа могла использовать
графические функции.
Главный цикл управляет всем моделированием:
repeat
AddCust;
AddQueue;
Display;
CheckOut;
Display;
if (time>30) and (time<50) then AddCust;
if (time>70) and (time<80) then begin
AddCust;
AddCust;
end;
time := time+1;
until time>100; { конец дня }
Функция AddCust использует либо Ran1, либо Ran2 для генера-
ции числа покупателей, встающих в очереди при каждом запросе.
Функция AddQuece используется для помещения покупателей в очереди
в соответствии с результатом Ran2, а также открывает новые очере-
ди, если все существующие переполнены. Функция Display отображает
программу моделирования. Checkout использует Ran2 для назначения
каждого покупателя в очередь с соответствующим увеличением счет-
чика очереди; каждый вызов уменьшает счетчик на 1. Когда счетчик
покупателей равен 0, очередь становится пустой.
Переменная time (время) изменяет интенсивность, с которой
генерируются покупатели, для того, чтобы отследить часовые пики.
Каждый проход по циклу представляет одну десятую часа.
На рис.7-4, 7-5 и 7-6 показаны состояния очередей, когда
time=28, time=60 и time=88, что соответствует нормальному време-
ни, концу первого пика и концу второго пика, соответственно. От-
метим, что в конце второго пика требуется максимум пять очередей,
Если моделирующая программа написана правильно, то в бакалейной
лавке оставшиеся пять очередей не нужны.
gueue 1: 10 time: 28
gueue 2: 8
gueue 3: 0
gueue 4: 0
gueue 5: 0
gueue 6: 0
gueue 7: 0
gueue 8: 0
gueue 9: 0
gueue 10: 0
Очередь ¦
¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
1 10
Рис.7-4. Состояние очередей, когда time=28:
gueue 1: 6 time: 60
gueue 2: 8
gueue 3: 8
gueue 4: 1
gueue 5: 0
gueue 6: 0
gueue 7: 0
gueue 8: 0
gueue 9: 0
gueue 10: 0
Очередь ¦ ¦
¦ ¦
¦ ¦ ¦
¦ ¦ ¦
¦ ¦ ¦
¦ ¦ ¦ ¦
1 10
Рис.7-5. Состояние очередей, когда time=60:
gueue 1: 8 time: 80
gueue 2: 9
gueue 3: 6
gueue 4: 6
gueue 5: 7
gueue 6: 0
gueue 7: 0
gueue 8: 0
gueue 9: 0
gueue 10: 0
Очередь ¦
¦ ¦
¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
1 10
Рис.7-6. Состояние очередей, когда time=88:
Вы можете непосредственно управлять различными переменными в
программе. Во-первых, вы можете изменить путь и число прибывающих
покупателей. Вы также можете изменить функцию AddCost, чтобы она
возвращала число покупателей в пиковые часы с большим или меньшим
постепенным увеличением или уменьшением. Программа предполагает,
что покупатели случайным образом выбирают, в какую очередь им
встать. Такой подход справедлив для одних покупателей, а другие
будут выбирать самую короткую очередь. Вы можете учесть это, из-
менив функцию AddQueue так, чтобы она в некоторых случаях помеща-
ла покупателей в самую короткую очередь, а в некоторых - случай-
ным образом. При моделировании не учитываются такие случайности,
как упавшая булка или буйный покупатель в очереди, которые вызы-
вают временные остановки очереди.
Целиком программа выглядит следующим образом:
program simulation; {моделирование очередей в бакалейной
лавке }
uses
Graph;
var
gueues, count: array[0..9] of integer;
open: array[0..9] of boolean;
cust, time: integer;
a1, a2: integer;
y,x: integer;
change: boolean;
GraphDriver, GraphMode: integer;
function Ran1: real;
var
t: real;
begin
t := (a1*32749+3) mod 32749;
a1 := Trunc(t);
Ran1 := Abs(t/32749);
end; {Ran1}
function Ran2: real;
var
t: real;
begin
t := (a2*10001+3) mod 17417;
a2 := Trunc(t);
Ran2 := Abs(t/17417);
end; {Ran2}
function CombRandom: real;
{random selection of generators} 2
var
f: real;
begin
f := Ran2;
if f>0.5 then CombRandom := Random
else CombRandom:=Ran1;{случайный выбор генераторов}
end; {CombRandom}
{ добавление покупателей в очередь }
procedure AddCust;
var
f, r: real;
begin
if change then f:=Random {переключение между двумя }
else f := Ran2; {генераторами }
if f>0.5 then
if f>0.6 then cust:=cust+1 {добавить одного покупателя}
else if f>0.7 then cust:=cust+2 {два покупателя}
else if f<0.8 then cust:=cust+3 {три покупателя }
else cust := cust+4; {четыре покупателя }
end; {AddCust}
{ обслуживание покупателя }
Procedure CheckOut;
var
t: integer;
begin
for t := 0 to 9 do
begin
if gueues[t]<>0 then
begin
{получить время обслуживания }
while count[t]=0 do count[t]:=Trunc(Ran1+5);
{новый покупатель требует времени обслуживания }
count[t]:=count[t]-1;
if count[t]=0 then gueues[t]:=gueues[t]-1;
{удаление покупателя}
end;
if gueues[t]=0 then open[t]:=FALSE;{закрытие очереди}
end;
end; {CheckOut}
{возвращается TRUE, если очередь переполнена }
function AllFull: Boolean;
var
t: integer;
begin
AllFull := TRUE;
for t := 0 to 9 do
if (gueues[t]<10) and open[t] then AllFull:=FALSE;
end; {AllFull}
{данная процедура вводит новые очереди }
procedure AddQueue;
var
t, line: integer;
done: Boolean;
begin
done := FALSE;
while cust<>0 do
begin
if AllFull then
begin
t:=0;
repeat
if not open[t] then
begin
open[t]:=TRUE;
done:=TRUE;
end;
t:=T+1;
until done or (t=9);
end
else
begin
Line:=Trunc(Ran2*10);
if open[line] and (gueues[line]<10) then
begin
gueues[line]:=gueues[line]+1;
cust:=cust-1;
end;
end;
if AllFull and open[9] then cust:=0; {all full}
end;
end; {AddQueue}
{очистить символы длины, начиная с позиции Х, У }
procedure ClrVed(x,y,len: integer);
var
i: integer;
s: string[80];
begin
for i := 1 to len do
s := concat(Chr(219), Chr(219));
SetColor(0);
OutTextXy(x, y, s);
SetColor(2);
end; {ClrVid}
{отображение экрана результатов моделирования очереди }
procedure Display;
var
t: integer;
value: string[80];
begin
cirVid(170, 10, 3);
str(time, value);
OutTextXy(120, 10, 'Time: ');
OutTextXy(170, 10, value);
for t := 0 to 9 do
begin
{erase old line}
SetColor(0);
Line((t*10)+160, 180, (t*10)+160, 180);
{нарисовать новое состояние моделирования }
SetColor(2);
Circle((t*10)+160, 180, 3);
Line((t*10)+160, 180, (t*10)+160, 180-gueues[t]*10);
{дать также текстовый вывод }
OutTextXy(8, t*10, 'gueue');
str(t+1, value);
value := concat(value, ':');
OutTextXy(56, t*10, value);
ClrVid(80, t*10, 3);
str(gueues[t], value);
OutTextXy(80, t*10, value);
end;
end; {Display}
begin
{переключение на графику, используя режим 4 CGA/EGA }
GraphDriver := CGA;
GraphMode := CGAC1;
InitGraph(GraphDriver, GraphMode, '');
SetColor(2);
SetLineStyle(SolidLn, 0, NormWidth);
a1:=1; a2:=203; {инициализация переменных генератора
случайных чисел }
change:=FALSE;
cust:=0;
time:=0;
for x := 0 to 9 do begin
gueues[x]:=0; {инициализировать очереди }
open[x]:=FALSE;{нет покупателей или очередей в начале
дня }
count[x]:=0; {счетчик очереди }
end;
OutTextXy(155, 190, '1 10');
OutTextXy(8, 190, 'Check-out lines; ');
{теперь начинается день
открытием первого пункта обслуживания
}
open[0]:=TRUE;
repeat
AddCust;
AddQueue;
Display;
CheckOut;
Display;
if (time>30) and (time<50) then AddCust;
if (time>70) and (time<80) then begin
AddCust;
AddCust;
end;
time:=time+1;
until time>100; { конец дня }
ReadLn;
RestoreCrtMode;
end.