使用C++TR1實現物流配送問題的簡單模擬

   物流配送問題是典型的NP完全問題,尋找求解該問題的高效準確的算法一直以來都是研究熱點。我在這裏不是討論解決該問題的具體算法,而是簡單介紹一下C++98的一個功能強大擴展--TR1
  TR1Technical Report 1的簡稱,它原本是標準委員會內部的一個名稱。它是在1998年標準委員會提出C++ Standard(就是我們說的"標準C++")之後 委員會擬定的下一個版本的C++ Standard應該具有的功能的一份描述。它僅是一份文檔,本身並沒有做出具體實現,但其中列出的這些實現很值得我們去了解。關於TR1Effective C++3e)》Item54有比較詳細的介紹,這裏不多說了。
  支持TR1的開發環境有:
        gcc4.*.*(gcc4.0及以後的版本)
      MSVC2008(vs2008及以後版本)

問題描述:   
    針對一般的分銷系統,即系統由分銷中心(DC),多個零售商組成,該系統的運營成本主要由運輸成本與庫存成本構成。分銷中心用自己的車輛爲各零售商供貨,而分銷中心由製造商直接供貨,假設零售商處的顧客需求是隨機的且服從一定的概率分佈,不同零售商之間以及同一零售商不同時期之間的需求是獨立的。一般DC與零售商均採用週期補貨策略,補貨時刻爲週期末,DC的一個補貨週期一般包含多個零售商的補貨週期。現考慮只有一個分銷中心和30個零售商組成的分銷系統,配送貨物爲單一產品。試就顧客需求服從參數爲6Possion分佈,銷售中心位置爲(0,0),30個零售商的位置可在[-200,200]‰[-200,200]的平面上隨機產生得到的分銷系統的運輸、配送策略建立數學模型,並以題目中提供的部分數據爲基礎,進行數據模擬。

程序實現:

#include <cstdio>
  #include <iostream>
  #include <fstream>
  #include <set>
  #include <vector>
  #include <random>
  using namespace std;
  using namespace std::tr1;
  
  #define PRINT_RES
  #define PRINT_STEP
  ofstream fout("res.txt");
  
  int nTests = 500; // 存放模擬次數
  const int N = 30; // 零售商
  const int Q = 18; // 車的載重量
  
  typedef double require_t;
  double x[N+1], y[N+1]; // 零售商的座標
  double W[N+1][N+1];        // 帶權鄰接矩陣
  require_t q[N+1];      // 零售商處的客戶需求
  
  inline double squre(double x) {    return x*x; }
  int main()
  {
     random_device rd;   // 隨機數引擎
     mt19937 gen(rd());  // 隨機數算法
     uniform_int<double> uniform(-200, 200); // 均勻分佈隨機數發生器
     poisson_distribution<double> poisson(6.0); // 泊松分佈隨機數發生器
     
     x[0] = y[0] = 0.0;
     double res=0;
     
     cout<<"請輸入試驗次數:";
     cin >> nTests; // 輸入模擬次數
     for(int t=1; t<=nTests; t++) {
         
         // 生成隨機數:
         int rc1 = 1, rc2 = 1;;
         set<double> sreq;
         set< pair<double, double> > pset;
         while( rc1 <= N || rc2 <= N ) {
             // 零售商位置 服從均勻分佈
             x[rc1] = uniform(gen);
             y[rc1] = uniform(gen);
             if( !(x[rc1] == 0.0 && y[rc1] ==0.0) ) {
                 pset.insert( pair<double, double>( x[rc1], y[rc1] ) );
                 rc1++;
             }
             // 客戶需求 服從泊松分佈
             q[rc2] = poisson(gen); 
             if( q[rc2] > 0.0 ) {
                 sreq.insert( q[rc2] );
                 rc2++;
             }
         }       
         
         // 更新鄰接矩陣:
         for(int i=0; i<=N; i++) {
             for(int j=0; j<=N; j++)  {
                 W[i][j] = sqrt( squre(x[i]-x[j]) + squre(y[i]-y[j]) );
             }
         }
  
         set<int> V;
         for(int i=1; i<=N; i++) V.insert(i);
         
         /***************************************************************************
          貪婪算法(greedy algorithm)
           時間複雜度:O(n^2*log2(n))
             Step1: 令S={},u=0, k=1;
             Step2: 令R(k)={u},Qt=Q;
             Step3: 構建集合AR(u)={e| e∈A(u)且e∈S,且q(e) < Qt };
             Step4: 如果AR(u)≠{},則
                         從AR(u)中找到到u的權最小的x,更新R(k),S,Qt,u:
                         R(k)=R(k)∪{x},S=S∪{x},Qt=Qt-q(x),u=x;
                         跳轉到(3);
                     否則,繼續(5);
             Step5: 如果S≠V,則k=k+1,u=0轉到(2);否則,結束;
         ****************************************************************************/
         // 1.   令S={},u=0, k=1;
         set<int> S;
         int u=0, k=1;       
         vector<int> R[N+1];           
  
         while(1) {
             // 2.令R(k)={u},Qt=Q;
             R[k] = vector<int>(1, u); 
             double Qt = Q;      
             while(1) {
                 // 3.構建集合AR(u)={e| e∈A(u)且e∈S,且q(e) < Qt }
                 set<int> AR;
                 for(int e=1; e<=N; e++) {
                     if( W[u][e]!=0 && q[e]<Qt && S.count(e)==0 )
                         AR.insert( e );
                 }
                 
                 // 4.從AR(u)中找到到u的權最小的x,更新R(k),S,Qt,u
                 if( AR.size() ) {
                     double minw = 0;
                     int minx=0;
                     set<int>::iterator it=AR.begin();
                     for( ;it != AR.end(); it++ ) 
                     {
                         if( q[*it]/W[u][*it] > minw ) {
                             minx = *it; 
                         }
                     }                   
                     R[k].push_back( minx );
                     S.insert( minx );
                     Qt -= q[ minx ];
                     u = minx;
                     continue;
                 }
                 else break;
             }
             // 5.如果S≠V,則k=k+1,u=0轉到(2);否則,結束;
             if( S != V ) {
                 if( V.size() - S.size() == 1 ) {
                     R[k+1] = vector<int>(2, 0);
                     R[k+1][1] = *V.begin();
                     break;
                 }
                 if( R[k].size()>1 ) k++; 
                 u=0;
                 continue;
             }
             else break;
         }
  
         // 計算本次模擬的 運營成本:
         double C=0;
          for(int i=1; i<=N; i++) {
              if( R[i].size() )  {
                  vector<int>::iterator it=R[i].begin();
                  for( ; it+1!=R[i].end(); it++) {
                      C += W[*it][*(it+1)];
                  }
                  C += W[*it][0];
              }
          }
         // 打印各次配送的路線:
          printf("\n第%d次模擬...\n", t);
          for(int i=0; i<=N; i++) {
              if( R[i].size() ) {
                  printf("第%d趟發貨:\t", i);
                  //int j=0, sz = R[i].size();
                  vector<int>::iterator it=R[i].begin();
                  for( ; it!=R[i].end(); it++) {
                      printf("%d\t", *it );// printf("(%d,%d)\t", *it, *(it+1) );
                  } // printf("(%d,%d)\t", *it, 0 );
                  printf("\n");
              }
          }
          printf("運輸成本: %g\n", C);
         fout << C << "\n";
         res += C;
     }
     printf("\n模擬次數:%d\n平均運輸成本:%g\n", nTests, res/nTests );
     return 0;
  }

 注:以上代碼只在vc2008中編譯通過,在gcc中編譯可能會出現問題(在我的gcc上沒有編譯成功,好像是gcc實現版的TR1所在的頭文件不太一樣)。

    這裏只用到了TR1的隨機數生成工具,它大大超越了rand(),它除了提供正態分佈以外,還提供了正態分佈、泊松分佈、伯努利分佈等概率分佈,而且使用起來也相當簡單。

這裏是關於TR1的一份清單:

EC++
Page
Effective C++, Third Edition
Name
TR1 Name Proposal
Document
265 Smart Pointers Smart Pointers n1450
265 tr1::function Polymorphic Function Wrappers n1402
266 tr1::bind Function Object Binders n1455
266 Hash Tables Unordered Associative Containers n1456
266 Regular Expressions Regular Expressions n1429
266 Tuples Tuple Types n1403 (PDF)
267 tr1::array Fixed Size Array n1479
267 tr1::mem_fn Function Template mem_fn n1432
267 tr1::reference_wrapper Reference Wrappers n1453
267 Random Number Generation Random Number Generation n1452
267 Mathematical Special Functions Mathematical Special Functions n1422
267 C99 Compatibility Extensions C Compatibility n1568
267 Type Traits Metaprogramming and Type Traits n1424
267 tr1::result_of Function Return Types n1454


關於TR1的描述的原始鏈接:http://www.aristeia.com/EC3E/TR1_info.html

 最新的C++標準已在去年發佈,即C++11.

關於C++11的一份清單(包括C++全部內容):

C++參考
C++98,C++03,C++11

語言

概念

實用工具庫

字符串庫

容器庫

array (C++11)  −  vector  −  deque
list  −  forward_list (C++11)
set  −  multiset
map  −  multimap
unordered_set (C++11)
unordered_multiset (C++11)
unordered_map (C++11)
unordered_multimap (C++11)
stack  −  queue  −  priority_queue

算法庫

迭代器庫

數值算法庫

輸入/輸出庫

本地化庫

正則表達式庫 (C++11)

原子操作庫 (C++11)

線程的支持庫 (C++11)

關於每個內容的詳細說明http://www.cplusplus.com/上也有不錯的說明(兩個網站的內容差不多).原始鏈接:http://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5

  但目前,支持C++11的編譯器並不多,只有:

       gcc 4.7.*

    VC2012 

   C++11有更多更強大、更有趣的內容供我們使用,我們要做的就是去熟悉。


發佈了31 篇原創文章 · 獲贊 40 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章