電梯仿真算法的C++實現

  【學習】關於電梯算法的C++實現

衆所周知,面向對象的程序設計更適合對現實生活中的描述,更加體現了軟件的工業化的精神,所以現在大部分的軟件開發工作都圍繞OOP的思想來進行的。但是在對現實生活中的實際問題,如何對所研究的系統進行面向對象的分析與設計呢?本篇文章以一個實際的例子,向大家介紹一下如何對實際問題進行分析和設計。

  一.問題描述:

  該實例是一個電梯載客問題,問題的描述如下:

  某貿易中心共10層,設有載客電梯1部。爲了處理問題的方便,有以下的限定條件:

  (1) 電梯的運行規則是:可到達每層。

  (2) 每部電梯的最大乘員量均爲K人(K值可以根據仿真情況在10~20人之間確定)。

  (3) 仿真開始時,電梯隨機地處於其符合運行規則的任意一層,爲空梯。

  (4) 仿真開始後,有N人(>20人)在該國際貿易中心的1層,開始乘梯活動。

  (5) 每個人初次所要到達的樓層是隨機的,開始在底層等待電梯到來。

  (6) 每個人乘坐電梯到達指定樓層後,再隨機地去往另一樓層,依此類推,當每人乘坐過L次(L值可以根據仿真情況在3~10次之間確定)電梯後,第L+1次爲下至底層並結束乘梯行爲。到所有人結束乘梯行爲時,本次仿真結束。

  (7) 電梯運行速度爲S秒/層(S值可以根據仿真情況在1~5之間確定),每人上下時間爲T秒(T值可以根據仿真情況在2~10之間確定)。

  (8) 電梯運行的方向由先發出請求者決定,不允許後發出請求者改變電梯的當前運行方向,除非是未被請求的空梯。

  最後開發的軟件要求:

  (1) 設計一個易於理解的界面,動態顯示各梯的載客與運行情況(上、下或停止),動態顯示各樓層的人員停留情況與要求乘梯情況;動態顯示從仿真開始到目前的時間。

  (2) 可變動的參數(K、N、M、L、S、T)應在程序開始時從對話框輸入
  二.系統分析與設計:

  採用OOP分析的關鍵就是要對問題的對象空間的分類(類的分析與設計,這一點跟面向過程的流程圖設計不太一樣),也就是在整個系統中包括那幾個類,每個類包含那些屬性特徵和行爲特徵。對於上面的電梯問題,很明顯有兩個類:即電梯類和乘客類(從所設計到的對象描述可以觀察到,但是有的問題即使這一步也要仔細分析)。電梯類應該反映所有關於電梯狀態和行爲的信息,而乘客類也應該反映這些信息。綜合上面問題的表述,現在將分析結果整理如下:
乘客類:

Cpassenger
{
bool bInLift; //是否在電梯裏
bool bSignal; //發出請求標誌
bool bStart; //仿真啓動標誌(false標誌仿真結束)
Cstring flag; //標識每個人得序號以及需求層數
Int iAtFloor; //所在樓層
Int iToFloor; //要去得樓層
Int iLifts; //已經乘坐電梯得次數了
Int number; //乘客得序號
}


  電梯類:
Celevator
{
bool bStart; //仿真開始標誌
int iAtFloor; //當前所處得層數得起點
int iToFloor; //即將去得層數
int iPassengers; //電梯裏得人數
bool bStop; //電梯停止標誌
bool bIsEmptyOperation; //電梯是否空載運行
CArray<CPassenger,CPassenger&> m_passengers; //存放載處於電梯裏面得乘客
}

  這裏說明一下,CArray<CPassenger,CPassenger&>是MFC裏面的一個模板集合類,第一個參數表示該集合所存儲的類別,第二個參數表示對該集合裏面的元素所採取的訪問方式,這裏採用的是引用的訪問方式,這種方式通過傳遞32位指針來進行訪問,它同時兼有地址訪問(效率高)和值傳遞的雙重優勢,現在一般對大的對象的存取一般提倡使用這種方式。

  另外,這裏也引用了view類,定義如下:

class CLiftsimulationView : public CFormView
{
UINT m_nTimer;
int k;
int n;
int l;
int s;
int t;
int floor;
CPassenger m_passenger[100]; //最大爲100個乘客
CElevator m_elevator; //一個電梯
int iSrcFloor,iDesFloor; //分別代表載客時得起始樓層和終結樓層,用在ontimer中
int iEmptySrcFloor,iEmptyDesFloor; //分別代表空載時得起始樓層和終結樓層,用在ontimer中
int itimes[41];
DWORD ElapseTime;
void DeleteColor(int src);
void DrawColor(int src,int increment);
};
  這裏大致把各個對象的成員設定出來了,爲了方便存儲,將所有的變量定義爲public的類型,這樣可以提高存儲的效率,當然了,它也破壞了OOP封裝的思想,降低了對象與對象之間的隔離性。這裏因爲問題不是很複雜,所以我們採用前面的方法。至此,類設計基本結束了,當然了,很多時候不是一開始就可以把類設計得很好,往往都是要先設計一部分,然後在後面得問題得處理中,還要對原來設計的類結構進行添加和刪除工作的。這裏要特別注意的是類設計的最終目的是降低系統的耦合,達到程序邏輯與數據之間的分離,更有利於代碼的編制和維護.
三.代碼編制:

  前面已經完成了類設計,那麼剩下來的就是應該是系統邏輯部分的實現了,對於系統整體來說,應該有一個事件偵測體系,用來對系統每個乘客和電梯的狀態的偵測,以便發送或者修改必要的信息,該偵測體系的週期定爲1秒鐘(可以用定時器實現),然後用串行的方式來模擬並行的。如果把思路總結一下,應該是跟動畫片的原理是一樣的:將多個固定和靜止的畫面定時、按順序地放映出來,就變成了活動的畫面。所以,可以在程序中定義一個主循環,在該循環外進行必要的初始化,進入後每秒鐘執行一次,以遍歷方式一一激勵當前已經產生的對象,由它們根據自己的當前狀態和相關的狀態變化規則,決定是否需要改變、改變成什麼樣的狀態,以及按照上述行爲特徵的設計展示必要的對象狀態。

  根據上面敘述的思想,下面列舉主要列出"事件偵測體系"的代碼:

  (應該在另外一個函數來觸發次函數,即調用SetTimer(1,1000,NULL)即可)

void CLiftsimulationView::OnTimer(UINT nIDEvent) //主要在這裏處理所有得邏輯
{ int i=1;
 int j=1;
 int a;
 int flag1=0;
 CString str1,str2;
 CString showtime,sen,m,h;
 DWORD dwTime;
 if (nIDEvent==1) //整個系統來驅動,這個是最小得時間單位
 {if (this->m_elevator.bStart =true) //電梯在仿真
  { //對所有乘客循環查詢,類似於消息循環,這是電梯核心程序得入口
   for(i=1;i<=this->n;i++)
   {if (this->m_passenger[i].bInLift ==false && this->m_passenger[i].bStart ==true &&  this->m_elevator.bStop==true && this->m_elevator.bIsEmptyOperation==true)
    { //輪詢各個乘客得請求
     //只有當電梯停止而且乘客在樓層上得時候,才能進行發信號
     if (this->m_passenger[i].bSignal ==true)
     {//初始化乘客 srand(::GetTickCount());
      a =rand()%10+1; //產生一個1到10得隨機數
      this->m_passenger[i].iToFloor =a; //該乘客要去得層數
      if (a==this->m_passenger[i].iAtFloor) //樓層一樣得話就放棄此次信號
       if (a ==10)
        this->m_passenger[i].iAtFloor=a-1;
       else
        this->m_passenger[i].iAtFloor=a+1;
        str1.Format("%d",this->m_passenger[i].iToFloor);
        str2.Format("%d",this->m_passenger[i].number );
        this->m_passenger[i].flag =str2+"--"+str1; this->m_passenger[i].bSignal =false;
        //關閉她得信號標誌,給別得乘客一個機會
        this->m_passenger[i].bInLift=true; //進入電梯了(虛擬)
        //初始化電梯,爲空載運行作準備
        this->m_elevator.iToFloor =this->m_passenger[i].iAtFloor;
        this->m_elevator.iPassengers =1;
        this->m_elevator.m_passengers.Add(this->m_passenger[i]);
        this->m_elevator.bStop =false; //表明電梯再運行 this->iEmptySrcFloor  =this->m_elevator.iAtFloor;
        this->iEmptyDesFloor =this->m_elevator.iToFloor ;       
        this->SetDlgItemText(IDC_STATIC18,(LPCTSTR)(str2+"--"+str1));
        //完成空載任務
        this->itimes[4]++;
        if (this->itimes[4]==1)
          SetTimer(4,2000,0); //電梯空載去接乘客
          break; //每次接一個
        }
       }
      }
     //判斷是否結束仿真
     for(i=1;i<=this->n;i++)
     { if (this->m_passenger[i].bStart ==true)
       flag1=1; }
     if (flag1==0) //結束仿真
     { KillTimer(1);
      this->m_elevator.bStart =false;
      return;
     }
     //防止沒有乘客發信號,系統進入死循環
     for(i=1;i<=this->n;i++)
     { if (this->m_passenger[i].bInLift ==false && this->m_elevator.bStop ==true && this->m_elevator.bStart ==true)
      { this->m_passenger[i].bSignal =true; //應該是隨機
        break;
      }
      }
     }
    else {
      KillTimer(1); //如果電梯運行標誌爲false,則停止仿真
    }
    dwTime=::GetTickCount() -this->ElapseTime ;
    dwTime=dwTime/1000;
    sen.Format("%d",dwTime%60);
    h.Format("%d",dwTime/3600);
    m.Format("%d",(dwTime/60)%60);
    showtime=h+" : "+m+" : "+sen;
    this->SetDlgItemText(IDC_STATIC16,showtime);
   }
   else if (nIDEvent==4) //電梯空載運行(去接乘客)
   { if (this->iEmptySrcFloor ==this->iEmptyDesFloor ) //到達目的地了
    { if (this->iEmptySrcFloor !=1 && this->iEmptySrcFloor !=10 )
     {
      this->DrawColor(iEmptySrcFloor,1);
      this->DrawColor(iEmptySrcFloor,-1);
     }
     //因爲到站了,所以要把電梯設置爲接收信號狀態
     this->m_elevator.bStop =true; //電梯不運行了
     this->m_elevator.bIsEmptyOperation =false; //爲滿載作準備
     this->m_elevator.iAtFloor =this->m_elevator.iToFloor ;
     this->m_elevator.iToFloor=this->m_elevator.m_passengers[0].iToFloor;
     KillTimer(4);
     this->itimes[4]=0;
     this->iSrcFloor =this->m_elevator.iAtFloor;
     this->iDesFloor =this->m_elevator.iToFloor ;
     this->m_elevator.bStop =false; //電梯啓動
     this->itimes[2]++;
     if (this->itimes[2]==1)
      SetTimer(2,2000 ,0); //啓動乘客上電梯 }
      if (iEmptySrcFloor <iEmptyDesFloor)
      { this->DrawColor(iEmptySrcFloor,1);
       iEmptySrcFloor++; }
      else {
       this->DrawColor(iEmptySrcFloor,-1);
       iEmptySrcFloor--;
       }
    }
    else if(nIDEvent==2) //電梯每隔3秒,電梯是載客運行,還有一種情況是無客運行
    {if (iSrcFloor <iDesFloor)
      { this->DrawColor(iSrcFloor,1);
       iSrcFloor++; }
    else if (iSrcFloor >iDesFloor)
    {
     this->DrawColor(iSrcFloor,-1);
     iSrcFloor--;
    }
    if (iSrcFloor ==iDesFloor) //到達目的地了
    { this->DrawColor(iSrcFloor,1);
     this->DrawColor(iSrcFloor,-1);
     //因爲到站了,所以要把電梯設置爲接收信號狀態
     this->m_elevator.bStop =true;
     this->m_elevator.iAtFloor =this->m_elevator.iToFloor ;
     //進行卸載乘客得操作
     srand(::GetTickCount());
     a =rand()%10+1; //產生一個1到10得隨機數
     this->m_elevator.m_passengers[0].iAtFloor =this->m_elevator.m_passengers[0].iToFloor ;
    this->m_elevator.m_passengers[0].iToFloor = a; //該乘客要去得層數
    this->m_elevator.m_passengers[0].iLifts++;
    if (this->m_elevator.m_passengers[0].iLifts ==this->l )
      this->m_elevator.m_passengers[0].bStart =false;
      this->m_elevator.m_passengers[0].bInLift =false; //電梯外面了
      this->m_elevator.m_passengers.RemoveAll();
      this->m_elevator.iPassengers --;
      //爲下一次空載作準備
      this->m_elevator.bStop =true;
      this->m_elevator.bIsEmptyOperation =true;
      KillTimer(2);
      this->itimes[2]=0; } }
    CFormView::OnTimer(nIDEvent);
} 


  四.結論:

  至此,全部的設計過程完畢,上面所有代碼已經在VC6.0+Win2000平臺下調試成功。從上面的分析可以看出,實際上運用OOP的思想進行設計的時候,關鍵是類設計,類設計一定要通過對系統對象域的劃分來進行,而關於類裏面的具體成員就必須由系統各個部分的功能來進行確定了。總之,類設計不是一天兩天的功夫,需要長期的實踐纔可能達到熟練的程度 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章