结构推理 离散事件模拟是一种常用模拟技术,用这种技术可以模拟现实世界的各种活动。假定你准备在一个繁华海滨开一个冰淇淋店,可首先试用计算机模拟这个冰淇淋店的运营过程。希望通过模拟对该店的规模(座位数)做出较好的决策。
【正确答案】(1)分析
   在模拟中,我们希望所实现的行为过程与真实事物的情况尽可能相似。因此,就需要决定一些问题。比如,这个店要多大,需要多少座位等。假设通过调查,我们已经得到了一些有关顾客来店频率的数据。例如,顾客以群为单位到达冰淇淋店,一群顾客的人数从一人到五人不等,二人一组或三人一组的最多,一人一组和多于三人的相对少一些。再假定顾客群以1~10分钟的间隔光临冰淇淋店,模拟过程中可以在这个范围里取一个随机值。另一方面,顾客到达后可能坐下来点食品,或者因为找不到座位而离去。如果坐下,他们将用2~10分钟点冰淇淋,随后将会在店里用餐15~35分钟。我们还知道,每个顾客将点1~3份冰淇淋,商店在每份冰淇淋上赚2.5元,等等。
   如何做出正确的选择呢?一种办法就是做一次模拟。在实现这种技术的程序里,优先队列常常被用做存储需要模拟的事件的基本数据结构。通常需要把事件按照其发生的先后顺序存入优先队列。由于优先队列的性质,其中最小元素将总是下一个要发生的事件。一个事件的发生又可能衍生出一些其他事件,这些事件也加入到优先队列中去。这样处理下去,直到所有事件都已发生,或是超过了预定的模拟时间,模拟过程结束。
   (2)实现
   1)数据结构
   顾客到达、顾客点食品、顾客离开等事件用Event结构体表示,它的成员包括事件类型(以上3种事件中的哪一种)、发生时间、此事件的顾客人数。
   typeclef struct {
   int type;
   int time;
   int customers;
   }Event;
   冰淇淋店也用Store结构体来表示,它的成员包括利润数和剩余的座位数。
   typedef struct {
   float profits;
   int freeChairs;
   }Store;
   用下面的堆(HeapPriorityQueue)来存储按发生时间为序的事件。
   typedef Event DataType;
   typedef struct{
       DataType element{MAXNUM];
       int n;    /*n<=MAXNUM,为优先队列中元素的个数*/
   }HeapPriorityQueue;
   typedef  HeapPriorityQueue*  PHeapPrio rityQueue;
   2)算法
   首先随机地构造一系列顾客到达事件,并把这些事件插入到堆里,然后启动run实现模拟过程。为了确定较合适的座位数,需要调整座位数,并对其进行多次模拟,最后确定使利润比较高同时资源(座位)利用率也比较高的方案。
   main(){
     int time,i,j,count[30];
     float pro[30];
     Event ae;
     int customersize;
     Store store;
     HeapPriorityQueue eventHeap;
     eventHeap.n=0;
     for(i=0;i<30;i++){COunt[i]=0;pro[i]=0;}
     for(j=0;j<30;j++){    /*模拟30次*/
       store.freeChairs=j+35:
       for(i=0;i<40. i++){
       limited=0;
       store.profits=0;
   for(time=0;time<480;time+=randombetween(2,5)){
                              /*建立模拟初试数据*/
       customersize=randombetween(1,5);
       ae.type=1:
       ae.time=time;
       ae.customers=customersize;
       add Heap(&eventHeap,ae);
     }
     run(&store.&eventHeap);  /*执行模拟*/
     pro[j]+=store.profits;
     if(limited)count[j]+=limited;
    }
   }
   for(j=0;j<30;j++){    /*输出结果*/
       pro[j]==pro[j]/40;
       printf("\n sits:/%d      profits:/%f',j+35,pro[j]);
       printf("limited:/%d".count[j]);
       }
     }
   其中,randombetween是用下面的函数生成需要的随机数:
       int randombetween(int low,int high){
       randomize();
       return low+random(high-10w+1);
       }
   run的功能是依次从堆中取出发生时间最小的事件,该时间发生时又产生其他事件(顾客到达后会点食品,点食品后过一段时间顾客吃完了就会离开),把它产生的事件加入到堆中。最后直到堆为空。
   void run(Store*store,HeapPriorityQueue*eventHeap){
     Event nextEvent;
     while(!isEmpty_Heap(eventHeap)){
       nextEvent.type=min_Heap(eventHeap).type;
       nextEvent.time=min_Heap(eventHeap).time;
       nextEvent.customers=min_Heap(eventHeap).customers;
       removeMin_Heap(eventHeap);
       processEvent(store,nextEvent,eventHeap);
       }
   }
   其中,processEvent(store,nextEvent,evemHe印)完成对于优先队列中事件的处理:
   void processEVent(Store*store,Event nextEvent,HeapPriorityQueue
     *eventHeap){
       Event oe;
       int orderSize,i;
       if(nextEvent.type==1){    /*处理arrive*/
         printf("\n customer group of size/%d arrive at time/%d",nextEvent.customers.nextEvent.time);
       if(nextEvent.custonlers<store->freeChairs){
         store->freeChairs-=nextEvent.customers;
         oe.time=nextEvent.time+randombetween(2,10);
         oe.customers=nextEvent.customers;
         oe.type=2;
         add_Heap(eventHeap,oe);
       }
      else{printf("\n no space:group leaves");limited++;}
     }
   if(nextEvent.type==2){    /*处理order*/
     for(orderSize=0,i=0;i<nextEvent.customers;i++)
       orderSize+=randombetween(1,3);
       printf("\ncustomer group of size/%d order/%d scoops of ice cream at time/%d",nextEvent.customers,orderSize,nextEvent.time);
       order(store,orderSize);
       oe.type=3;
       oe.time=nextEvent.time+randombetween(15,35);
       oe.customers=nextEvent.customers;
       add_Heap(eventHeap,oe);
       }
       if(nextEvent.type==3){    /*处理leave*/
       printf("\ncustomer group of size/%d leave at time/%d",nextEvent.customers.nextEvent.time);
       store->freeChairs+=nextEvent.customers;
   }
   }
   剩下的order(store,orderSize)可以如下定义:
   void order(Store*store,int orderSize)
   {store->profits+=orderSize*2.5;}
   3)结果
   以下是程序的某次执行的结果,这3栏分别对应座位数、利润、座位不足的次数。这些数据表明:座位教为50~60时,座位的利用率较高,并且利润也比较高。
   sits:35 profits:1539.4375000  limited:881
   sits:36 profits:1882.3125000  limited:653
   sits:37 profits:2036.8750000  limited:926
   sits:38 profits:1738.3125000  limited:816
   sits:39 profits:1728.1875000  limited:612
   sits:40 profits:1777.9375000  limited:737
   sits:41 profits:1895.0625000  limited:341
   sits:42 profits:1853.1250000  1imited:362
   sits:43 profits:1862.3750000  limited:542
   sits:44 profits:1785.9375000  limited:668
   sits:45 profits:1658.3750000  limited:494
   sits:46 profits:2135.3750000  limited:311
   sits:47 profits:1851.5000000  limited:368
   sits:48 profits:2153.5625000  limited:286
   sits:49 profits:1901.7500000  limited:297
   sits:50 profits:2131.9375000  limited:433
   sits:51 profits:2036.6875000  limited:463
   sits:52 profits:2000.6875000  limited:244
   sits:53 profits:2061.8750000  1imited:277
   sits:54 profits:1888.1875000  limited:154
   sits:55 profits:2105.7500000  limited:258
   sits:56 profits:2275.9375000  1imited:291
   sits:57 profits:1956.3750000  limited:163
   sits:58 profits:2068.0625000  1imited:132
   sits:59 profits:1792.5625000  limited:92
   sits:60 profits:2262.5625000  limited:329
   sits:61 profits:2189.5000000  limited:149
   sits:62 profits:2247.7500000  limited:196
   sits:63 profits:2308.3125000  limited:332
   sits:64 profits:2492.4375000  limited:227
【答案解析】