Энциклопедия Turbo Pascal. Главы 5-8
Страница 29. Моделирование очередей обслуживания


Моделирование очередей обслуживания

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

 
« Предыдущая статья   Следующая статья »