Страница 29 из 37
Моделирование очередей обслуживания В первом примере моделируется обслуживание в бакалейной лав- ке. Предположим, что лавка открыта 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.
|