情報学部 菅沼ホーム 目次 索引

待ち行列(簡単な例)

    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

  プログラムは,待ち行列が 1 つで,かつ,サービス窓口の数が s (任意)であるような非常に簡単な待ち行列モデル

をシミュレーションするためのものです.客の到着やサービスの分布は,すべて指数分布となっています(修正は,非常に簡単だと思います).また,実行結果のカッコ内の値は,対応する項目に対する理論値です.なお,このプログラムでは,クラスを使用しています.

  1. C++

      C++11 においては,「メルセンヌ・ツイスター法による擬似乱数生成エンジン」を使用することができます.
    /******************************/
    /* 簡単な待ち行列問題(M/M/s)*/
    /*      coded by Y.Suganuma   */
    /******************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctime>
    #include <math.h>
    #include <map>
    #include <queue>
    #include "MT.h"
    using namespace std;
    
    /************************/
    /* クラスCustomerの定義 */
    /************************/
    class Customer
    {
    	public :
    		double time;   // 到着時刻
    		int state;   // 客の状態
                         //   =-1 : 待ち行列に入っている
                         //   >=0 : サービスを受けている窓口番号
    		/*********************/
    		/* コンストラクタ    */
    		/*      s : 状態     */
    		/*      t : 到着時刻 */
    		/*******************************/
    		Customer::Customer(int s, double t)
    		{
    			time  = t;
    			state = s;
    		}
    };
    
    /**********************/
    /* クラスQ_baseの定義 */
    /**********************/
    class Q_base {
    		int s;   // 窓口の数
    		int asb;   // 全窓口の空き状況
                       //    =0 : すべての窓口がふさがっている
                       //    =n : n個の窓口が空いている
    		int *sb;   // 各窓口の空き状況
                       //    =0 : 窓口は空いている
                       //    >0 : サービスを受けている客番号
    		double asb_t;   // すべての窓口がふさがった時刻
    		double c_asb;   // すべての窓口がふさがっている時間の累計
    		double *c_sb;   // 各窓口がふさがっている時間の累計
    		double *st_e;   // 各窓口のサービス終了時刻
    		double *st_s;   // 各窓口がふさがった時刻
    		int m_lq;   // 最大待ち行列長
    		double c_lq_t;   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    		double c_wt;   // 待ち時間の累計
    		double lq_t;   // 現在の待ち行列長になった時刻
    		double m_wt;   // 最大待ち時間
    		double c_sc_t;   // 系内客数にその数が継続した時間を乗じた値の累計
    		double c_sys;   // 滞在時間の累計
    		double m_sys;   // 最大滞在時間
    		double sc_t;   // 現在の系内客数になった時刻
    		int m_sc;   // 最大系内客数
    		double m_a;   // 到着時間間隔の平均値
    		double m_s;   // サービス時間の平均値
    		double at;   // 次の客の到着時刻(負の時は,終了)
    		double p_time;   // 現在時刻
    		double end;   // シミュレーション終了時刻
    		int nc;   // 到着客数カウンタ
    		map<int, Customer> cus;   // 系内にいる客のリスト
    		queue<int> que;   // 待ち行列
    	public:
    		Q_base(int, double, double, double);   // コンストラクタ
    		~Q_base();   // デストラクタ
    		double Next_at();   // 次の到着時刻
    		double Next_sv();   // サービス終了時刻
    		void Control();   // 全体の制御
    		int Next();   // 次の処理の決定
    		int End_o_s();   // 終了処理
    		void Arrive();   // 客の到着処理
    		void Service(int);   // サービス終了
    		void Output();   // 出力
    };
    
    /*****************************************/
    /* コンストラクタ                        */
    /*      s_i : 窓口の数                   */
    /*      m_a_i : 到着時間間隔の平均値     */
    /*      m_s_i : サービス時間の平均値     */
    /*      end_i : シミュレーション終了時刻 */
    /*****************************************/
    Q_base::Q_base (int s_i, double m_a_i, double m_s_i, double end_i)
    {
    /*
              設定
    */
    	s   = s_i;
    	m_a = m_a_i;
    	m_s = m_s_i;
    	end = end_i;
    /*
              領域の確保
    */
    	sb   = new int [s];
    	c_sb = new double [s];
    	st_e = new double [s];
    	st_s = new double [s];
    
    	for (int i1 = 0; i1 < s; i1++) {
    		sb[i1]   = 0;
    		c_sb[i1] = 0.0;
    	}
    /*
              初期設定
    */
    	p_time = 0.0;
    	nc     = 0;
    	asb    = s;
    	m_lq   = 0;
    	m_sc   = 0;
    	c_asb  = 0.0;
    	c_wt   = 0.0;
    	m_wt   = 0.0;
    	c_lq_t = 0.0;
    	lq_t   = 0.0;
    	m_sys  = 0.0;
    	c_sys  = 0.0;
    	c_sc_t = 0.0;
    	sc_t   = 0.0;
    /*
              乱数の初期設定
    */
    	init_genrand((unsigned)time(NULL));
    /*
              最初の客の到着時刻の設定
    */
    	at = p_time + Next_at();
    }
    
    /****************/
    /* デストラクタ */
    /****************/
    Q_base::~Q_base()
    {
    	delete [] sb;
    	delete [] c_sb;
    	delete [] st_e;
    	delete [] st_s;
    }
    
    /********************************/
    /* 次の客の到着までの時間の発生 */
    /********************************/
    double Q_base::Next_at()
    {
    	return -m_a * log(genrand_real3());
    }
    
    /************************/
    /* サービス時間の発生   */
    /************************/
    double Q_base::Next_sv()
    {
    	return -m_s * log(genrand_real3());
    }
    
    /**************/
    /* 全体の制御 */
    /**************/
    void Q_base::Control()
    {
    	int sw = 0;
    	while (sw > -2) {
    		sw = Next();   // 次の処理の選択
    		if (sw == -1)
    			sw = End_o_s();   // シミュレーションの終了
    		else {
    			if (sw == 0)
    				Arrive();   // 客の到着処理
    			else
    				Service(sw);   // サービスの終了
    		}
    	}
    }
    
    /**************************************************/
    /* 次の処理の決定                                 */
    /*      return : =-1 : シミュレーションの終了     */
    /*               =0  : 客の到着処理               */
    /*               =i  : i番目の窓口のサービス終了 */
    /**************************************************/
    int Q_base::Next()
    {
    	int sw = -1;
    	double t  = end;   // シミュレーション終了時刻で初期設定
    					// 次の客の到着時刻
    	if (at >= 0.0 && at < t) {
    		sw = 0;
    		t  = at;
    	}
    					// サービス終了時刻
    	for (int i1 = 0; i1 < s; i1++) {
    		if (sb[i1] > 0 && st_e[i1] <= t) {
    			sw = i1 + 1;
    			t  = st_e[i1];   // 窓口i1のサービス終了時刻
    		}
    	}
    
    	return sw;
    }
    
    /**********************************/
    /* 終了処理                       */
    /*      return : =-1 : 終了前処理 */
    /*               =-2 : 実際の終了 */
    /**********************************/
    int Q_base::End_o_s()
    {
    	int sw = -2;
    	p_time = end;   // 現在時刻
    	at     = -1.0;   // 次の客の到着時刻
    
    	for (int i1 = 0; i1 < s; i1++) {
    		if (sb[i1] > 0) {   // サービス中の客はいるか?
    			if (sw == -2) {
    				sw  = -1;
    				end = st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    			else {
    				if (st_e[i1] > end)
    					end = st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    		}
    	}
    
    	return sw;
    }
    
    /****************/
    /* 客の到着処理 */
    /****************/
    void Q_base::Arrive()
    {
    /*
              客数の増加と次の客の到着時刻の設定
    */
    	nc     += 1;   // 到着客数カウンタ
    	p_time  = at;   // 現在時刻
    	at      = p_time + Next_at();      // 次の客の到着時刻
    	if (at >= end)
    		at = -1.0;
    /*
              系内客数の処理
    */
    	c_sc_t += cus.size() * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    	sc_t    = p_time;   // 現在の系内客数になった時刻
    	if ((int)cus.size()+1 > m_sc)
    		m_sc = cus.size() + 1;   // 最大系内客数
    /*
              窓口に空きがない場合
    */
    	if (asb == 0) {
    		Customer ct_p(-1, p_time);
    		cus.insert(pair<int, Customer>(nc, ct_p));   // 客の登録(系内客数)
    		c_lq_t += que.size() * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    		que.push(nc);   // 客の登録(待ち行列)
    		lq_t    = p_time;   // 現在の待ち行列長になった時刻
    		if ((int)que.size() > m_lq)
    			m_lq = que.size();   // 最大待ち行列長
    	}
    /*
              すぐサービスを受けられる場合
    */
    	else {
    		int k = -1;
    		for (int i1 = 0; i1 < s && k < 0; i1++) {
    			if (sb[i1] == 0) {
    				Customer ct_p(i1, p_time);
    				cus.insert(pair<int, Customer>(nc, ct_p));   // 客の登録(系内客数)
    				k        = i1;
    				sb[k]    = nc;   // サービスを受けている客番号
    				st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    				asb     -= 1;   // 空いている窓口数
    			}
    		}
    		st_s[k] = p_time;   // 窓口kがふさがった時刻
    		if (asb == 0)
    			asb_t = p_time;   // すべての窓口がふさがった時刻
    	}
    }
    
    /*********************************/
    /* サービス終了時の処理          */
    /*      k : サービス終了窓口番号 */
    /*********************************/
    void Q_base::Service(int k)
    {
    /*
              時間の設定
    */
    	k      -= 1;
    	p_time  = st_e[k];   // 現在時刻
    	st_e[k] = -1.0;   // サービス終了時間
    /*
              系内客数の処理
    */
    	c_sc_t += cus.size() * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    	sc_t    = p_time;   // 現在の系内客数になった時刻
    /*
              滞在時間の処理
    */
    	map<int, Customer>::iterator it = cus.find(sb[k]);
    	double x1 = p_time - (it->second).time;
    	c_sys += x1;   // 滞在時間の累計
    	if (x1 > m_sys)
    		m_sys = x1;   // 最大滞在時間
    	cus.erase(sb[k]);   // 客の削除(系内客数)
    /*
              窓口使用時間の処理
    */
    	asb     += 1;   // 空いている窓口数
    	sb[k]    = 0;   // 窓口kを空き状態にする
    	c_sb[k] += (p_time - st_s[k]);   // 窓口kがふさがっている時間の累計
    /*
              待ち行列がある場合
    */
    	if (que.size() > 0) {
    		asb    -= 1;   // 開いている窓口数
    		c_lq_t += que.size() * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    		int n = que.front();
    		que.pop();   // 客の削除(待ち行列)
    		lq_t    = p_time;   // 現在の待ち行列長になった時刻
    		it      = cus.find(n);
    		x1      = p_time - (it->second).time;
    		c_wt   += x1;   // 待ち時間の累計
    		if (x1 > m_wt)
    			m_wt = x1;   // 最大待ち時間
    		int k = -1;
    		for (int i1 = 0; i1 < s && k < 0; i1++) {
    			if (sb[i1] == 0) {
    				k        = i1;
    				sb[k]    = n;   // 窓口kの客番号
    				st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    				st_s[k]  = p_time;   // 窓口kがふさがった時刻
    				(it->second).state = k;   // 客の状態変更
    			}
    		}
    	}
    /*
              待ち行列がない場合
    */
    	else {
    		if (asb == 1)
    			c_asb += (p_time - asb_t);   // すべての窓口がふさがっている時間の累計
    	}
    }
    
    /************************/
    /* 結果の出力           */
    /* (カッコ内は理論値) */
    /************************/
    void Q_base::Output()
    {
    	double rn  = (double)nc;
    	double rs  = (double)s;
    	double ram = 1.0 / m_a;
    	double myu = 1.0 / m_s;
    	double rou = ram / (rs * myu);
    	double p0, pi;
    	if (s == 1) {
    		p0 = 1.0 - rou;
    		pi = rou;
    	}
    	else {
    		p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)));
    		pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou));
    	}
    	double Lq = pi * rou / (1.0 - rou);
    	double L  = Lq + rs * rou;
    	double Wq = Lq / ram;
    	double W  = Wq + 1.0 / myu;
    	printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n",
               p_time, nc, rou, p0);
    	printf("   すべての窓口が塞がっている割合=%.3f (%.3f)\n",
               c_asb/p_time, pi);
    	printf("   各窓口が塞がっている割合\n");
    	for (int i1 = 0; i1 < s; i1++)
    		printf("      %d   %.3f\n", i1+1, c_sb[i1]/p_time);
    	printf("   平均待ち行列長=%.3f (%.3f)  最大待ち行列長=%d\n",
               c_lq_t/p_time, Lq, m_lq);
    	printf("   平均系内客数  =%.3f (%.3f)  最大系内客数  =%d\n",
               c_sc_t/p_time, L, m_sc);
    	printf("   平均待ち時間  =%.3f (%.3f)  最大待ち時間  =%.3f\n",
               c_wt/rn, Wq, m_wt);
    	printf("   平均滞在時間  =%.3f (%.3f)  最大滞在時間  =%.3f\n",
               c_sys/rn, W, m_sys);
    }
    
    /****************/
    /* main program */
    /****************/
    int main()
    {
    	int s;
    	double end, m_a, m_s;
    /*
              入力データ
    */
    	printf("窓口の数は? ");
    	scanf("%d", &s);
    
    	printf("シミュレーション終了時間? ");
    	scanf("%lf", &end);
    
    	printf("   到着時間間隔の平均値は? ");
    	scanf("%lf", &m_a);
    
    	printf("   サービス時間の平均値は? ");
    	scanf("%lf", &m_s);
    /*
              システムの定義
    */
    	Q_base base(s, m_a, m_s, end);
    /*
              シミュレーションの実行
    */
    	base.Control();
    /*
              出力
    */
    	base.Output();
    
    	return 0;
    }
    
    //---------------------MT.h---------------------------
    //   A C-program for MT19937, with initialization improved 2002/1/26.
    //   Coded by Takuji Nishimura and Makoto Matsumoto.
    //
    //   Before using, initialize the state by using init_genrand(seed)  
    //   or init_by_array(init_key, key_length).
    //
    //   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
    //   All rights reserved.                          
    //
    //   Redistribution and use in source and binary forms, with or without
    //   modification, are permitted provided that the following conditions
    //   are met:
    //
    //     1. Redistributions of source code must retain the above copyright
    //        notice, this list of conditions and the following disclaimer.
    //
    //     2. Redistributions in binary form must reproduce the above copyright
    //        notice, this list of conditions and the following disclaimer in the
    //        documentation and/or other materials provided with the distribution.
    //
    //     3. The names of its contributors may not be used to endorse or promote 
    //        products derived from this software without specific prior written 
    //        permission.
    //
    //   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    //   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    //   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    //   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    //   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    //   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    //   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    //   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    //   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    //   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    //   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    //
    //
    //   Any feedback is very welcome.
    //   http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
    //   email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
    
    
    //   The original version of http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c was modified by Takahiro Omi as
    //   - delete line 47 "#include<stdio.h>"
    //   - delete line 174 int main(void){...}
    //   - change N -> MT_N
    //   - change N -> MT_N
    //   - change the file name "mt19937ar.c" -> "MT.h"
    
    
    /*
    // Period parameters
    #define MT_N 624
    #define MT_M 397
    #define MATRIX_A 0x9908b0dfUL   // constant vector a
    #define UPPER_MASK 0x80000000UL // most significant w-r bits
    #define LOWER_MASK 0x7fffffffUL // least significant r bits
    
    static unsigned long mt[MT_N]; // the array for the state vector
    static int mti=MT_N+1; // mti==MT_N+1 means mt[MT_N] is not initialized
    
    // initializes mt[MT_N] with a seed
    void init_genrand(unsigned long s)
    {
        mt[0]= s & 0xffffffffUL;
        for (mti=1; mti<MT_N; mti++) {
            mt[mti] = 
    	    (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
            // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
            // In the previous versions, MSBs of the seed affect
            // only MSBs of the array mt[].
            // 2002/01/09 modified by Makoto Matsumoto
            mt[mti] &= 0xffffffffUL;
            // for >32 bit machines
        }
    }
    
    // initialize by an array with array-length
    // init_key is the array for initializing keys
    // key_length is its length
    // slight change for C++, 2004/2/26
    void init_by_array(unsigned long init_key[], int key_length)
    {
        int i, j, k;
        init_genrand(19650218UL);
        i=1; j=0;
        k = (MT_N>key_length ? MT_N : key_length);
        for (; k; k--) {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
              + init_key[j] + j; // non linear
            mt[i] &= 0xffffffffUL; // for WORDSIZE > 32 machines
            i++; j++;
            if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; }
            if (j>=key_length) j=0;
        }
        for (k=MT_N-1; k; k--) {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
              - i; // non linear
            mt[i] &= 0xffffffffUL; // for WORDSIZE > 32 machines
            i++;
            if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; }
        }
    
        mt[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
    }
    
    // generates a random number on [0,0xffffffff]-interval
    unsigned long genrand_int32(void)
    {
        unsigned long y;
        static unsigned long mag01[2]={0x0UL, MATRIX_A};
        // mag01[x] = x * MATRIX_A  for x=0,1
    
        if (mti >= MT_N) { // generate N words at one time
            int kk;
    
            if (mti == MT_N+1)   // if init_genrand() has not been called,
                init_genrand(5489UL); // a default initial seed is used
    
            for (kk=0;kk<MT_N-MT_M;kk++) {
                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+MT_M] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            for (;kk<MT_N-1;kk++) {
                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+(MT_M-MT_N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            y = (mt[MT_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
            mt[MT_N-1] = mt[MT_M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
    
            mti = 0;
        }
      
        y = mt[mti++];
    
        // Tempering
        y ^= (y >> 11);
        y ^= (y << 7) & 0x9d2c5680UL;
        y ^= (y << 15) & 0xefc60000UL;
        y ^= (y >> 18);
    
        return y;
    }
    
    // generates a random number on [0,0x7fffffff]-interval
    long genrand_int31(void)
    {
        return (long)(genrand_int32()>>1);
    }
    
    // generates a random number on [0,1]-real-interval
    double genrand_real1(void)
    {
        return genrand_int32()*(1.0/4294967295.0); 
        // divided by 2^32-1
    }
    
    // generates a random number on [0,1)-real-interval
    double genrand_real2(void)
    {
        return genrand_int32()*(1.0/4294967296.0); 
        // divided by 2^32
    }
    
    // generates a random number on (0,1)-real-interval
    double genrand_real3(void)
    {
        return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); 
        // divided by 2^32
    }
    
    // generates a random number on [0,1) with 53-bit resolution
    double genrand_res53(void) 
    { 
        unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; 
        return(a*67108864.0+b)*(1.0/9007199254740992.0); 
    } 
    // These real versions are due to Isaku Wada, 2002/01/09 added
    */
    			

  2. Java

    /******************************/
    /* 簡単な待ち行列問題(M/M/s)*/
    /*      coded by Y.Suganuma   */
    /******************************/
    import java.io.*;
    import java.util.*;
    
    public class Test
    {
    	/****************/
    	/* main program */
    	/****************/
    	public static void main(String args[]) throws IOException
    	{
    		int s;
    		double end, m_a, m_s;
    		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    	/*
    	          入力データ
    	*/
    		System.out.print("窓口の数は? ");
    		s = Integer.parseInt(in.readLine());
    
    		System.out.print("シミュレーション終了時間? ");
    		end = Double.parseDouble(in.readLine());
    
    		System.out.print("   到着時間間隔の平均値は? ");
    		m_a = Double.parseDouble(in.readLine());
    
    		System.out.print("   サービス時間の平均値は? ");
    		m_s = Double.parseDouble(in.readLine());
    	/*
    	          システムの定義
    	*/
    		Q_base base = new Q_base(s, m_a, m_s, end);
    	/*
    	          シミュレーションの実行
    	*/
    		base.Control();
    	/*
    	          出力
    	*/
    		base.Output();
    	}
    }
    
    /**********************/
    /* クラスQ_baseの定義 */
    /**********************/
    class Q_base {
    	private int s;   // 窓口の数
    	private int asb;   // 全窓口の空き状況
                           //    =0 : すべての窓口がふさがっている
                           //    =n : n個の窓口が空いている
    	private int [] sb;   // 各窓口の空き状況
                             //    =0 : 窓口は空いている
                             //    >0 : サービスを受けている客番号
    	private double asb_t;   // すべての窓口がふさがった時間
    	private double c_asb;   // すべての窓口がふさがっている時間の累計
    	private double [] c_sb;   // 各窓口がふさがっている時間の累計
    	private double [] st_e;   // 各窓口のサービス終了時間
    	private double [] st_s;   // 各窓口がふさがった時間
    	private int m_lq;   // 最大待ち行列長
    	private double c_lq_t;   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    	private double c_wt;   // 待ち時間の累計
    	private double lq_t;   // 現在の待ち行列長になった時間
    	private double m_wt;   // 最大待ち時間
    	private double c_sc_t;   // 系内客数にその数が継続した時間を乗じた値の累計
    	private double c_sys;   // 滞在時間の累計
    	private double m_sys;   // 最大滞在時間
    	private double sc_t;   // 現在の系内客数になった時間
    	private int m_sc;   // 最大系内客数
    	private double m_a;   // 到着時間間隔の平均値
    	private double m_s;   // サービス時間の平均値
    	private double at;   // 次の客の到着時間(負の時は,終了)
    	private double p_time;   // 現在時間
    	private double end;   // シミュレーション終了時間
    	private int nc;   // 到着客数カウンタ
    	private Random rn;   // 乱数
    	private TreeMap <Integer, Customer> cus;   // 系内にいる客のリスト
    	private ArrayDeque <Integer> que;   // 待ち行列
    
    	/*****************************************/
    	/* コンストラクタ                        */
    	/*      s_i : 窓口の数                   */
    	/*      m_a_i : 到着時間間隔の平均値     */
    	/*      m_s_i : サービス時間の平均値     */
    	/*      end_i : シミュレーション終了時間 */
    	/*****************************************/
    	Q_base (int s_i, double m_a_i, double m_s_i, double end_i)
    	{
    	/*
    	          設定
    	*/
    		s   = s_i;
    		m_a = m_a_i;
    		m_s = m_s_i;
    		end = end_i;
    	/*
    	          領域の確保
    	*/
    		sb   = new int [s];
    		c_sb = new double [s];
    		st_e = new double [s];
    		st_s = new double [s];
    
    		for (int i1 = 0; i1 < s; i1++) {
    			sb[i1]   = 0;
    			c_sb[i1] = 0.0;
    		}
    
    		cus = new TreeMap <Integer, Customer> ();
    		que = new ArrayDeque <Integer> ();
    	/*
    	          初期設定
    	*/
    		p_time = 0.0;
    		nc     = 0;
    		asb    = s;
    		m_lq   = 0;
    		m_sc   = 0;
    		c_asb  = 0.0;
    		c_wt   = 0.0;
    		m_wt   = 0.0;
    		c_lq_t = 0.0;
    		lq_t   = 0.0;
    		m_sys  = 0.0;
    		c_sys  = 0.0;
    		c_sc_t = 0.0;
    		sc_t   = 0.0;
    	/*
    	          乱数の初期設定
    	*/
    		rn = new Random();
    	/*
    	          最初の客の到着時間の設定
    	*/
    		at = p_time + Next_at();
    	}
    
    	/**********************************/
    	/* 次の客の到着までの時間の発生   */
    	/**********************************/
    	double Next_at()
    	{
    		return -m_a * Math.log(rn.nextDouble());
    	}
    
    	/************************/
    	/* サービス時間の発生   */
    	/************************/
    	double Next_sv()
    	{
    		return -m_s * Math.log(rn.nextDouble());
    	}
    
    	/**************/
    	/* 全体の制御 */
    	/**************/
    	void Control()
    	{
    		int sw = 0;
    		while (sw > -2) {
    			sw = Next();   // 次の処理の選択
    			if (sw == -1)
    				sw = End_o_s();   // シミュレーションの終了
    			else {
    				if (sw == 0)
    					Arrive();   // 客の到着処理
    				else
    					Service(sw);   // サービスの終了
    			}
    		}
    	}
    
    	/**************************************************/
    	/* 次の処理の決定                                 */
    	/*      return : =-1 : シミュレーションの終了     */
    	/*               =0  : 客の到着処理               */
    	/*               =i  : i番目の窓口のサービス終了 */
    	/**************************************************/
    	int Next()
    	{
    		int sw = -1;
    		double t  = end;   // シミュレーション終了時刻で初期設定
    					// 次の客の到着時刻
    		if (at >= 0.0 && at < t) {
    			sw = 0;
    			t  = at;
    		}
    					// サービス終了時刻
    		for (int i1 = 0; i1 < s; i1++) {
    			if (sb[i1] > 0 && st_e[i1] <= t) {
    				sw = i1 + 1;
    				t  = st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    		}
    
    		return sw;
    	}
    
    	/**********************************/
    	/* 終了処理                       */
    	/*      return : =-1 : 終了前処理 */
    	/*               =-2 : 実際の終了 */
    	/**********************************/
    	int End_o_s()
    	{
    		int sw = -2;
    		p_time = end;   // 現在時刻
    		at     = -1.0;   // 次の客の到着時刻
    
    		for (int i1 = 0; i1 < s; i1++) {
    			if (sb[i1] > 0) {   // サービス中の客はいるか?
    				if (sw == -2) {
    					sw  = -1;
    					end = st_e[i1];   // 窓口i1のサービス終了時刻
    				}
    				else {
    					if (st_e[i1] > end)
    						end = st_e[i1];   // 窓口i1のサービス終了時刻
    				}
    			}
    		}
    
    		return sw;
    	}
    
    	/****************************/
    	/* 客の到着処理             */
    	/*      ct : 客リストの先頭 */
    	/****************************/
    	void Arrive()
    	{
    	/*
    	          客数の増加と次の客の到着時刻の設定
    	*/
    		nc     += 1;   // 到着客数カウンタ
    		p_time  = at;   // 現在時刻
    		at      = p_time + Next_at();      // 次の客の到着時刻
    		if (at >= end)
    			at = -1.0;
    	/*
    	          系内客数の処理
    	*/
    		c_sc_t += cus.size() * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    		sc_t    = p_time;   // 現在の系内客数になった時刻
    		if (cus.size()+1 > m_sc)
    			m_sc = cus.size() + 1;   // 最大系内客数
    	/*
    	          窓口に空きがない場合
    	*/
    		if (asb == 0) {
    			Customer ct_p = new Customer(-1, p_time);
    			cus.put(new Integer(nc), ct_p);   // 客の登録(系内客数)
    			c_lq_t += que.size() * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			que.add(new Integer(nc));   // 客の登録(待ち行列)
    			lq_t    = p_time;   // 現在の待ち行列長になった時刻
    			if (que.size() > m_lq)
    				m_lq = que.size();   // 最大待ち行列長
    		}
    	/*
    	          すぐサービスを受けられる場合
    	*/
    		else {
    			int k = -1;
    			for (int i1 = 0; i1 < s && k < 0; i1++) {
    				if (sb[i1] == 0) {
    					Customer ct_p = new Customer(i1, p_time);
    					cus.put(new Integer(nc), ct_p);   // 客の登録(系内客数)
    					k        = i1;
    					sb[k]    = nc;   // サービスを受けている客番号
    					st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    					asb     -= 1;   // 空いている窓口数
    				}
    			}
    			st_s[k] = p_time;   // 窓口kがふさがった時刻
    			if (asb == 0)
    				asb_t = p_time;   // すべての窓口がふさがった時刻
    		}
    	}
    
    	/*********************************/
    	/* サービス終了時の処理          */
    	/*      k : サービス終了窓口番号 */
    	/*      ct : 客リストの先頭      */
    	/*********************************/
    	void Service(int k)
    	{
    	/*
    	          時間の設定
    	*/
    		k      -= 1;
    		p_time  = st_e[k];   // 現在時刻
    		st_e[k] = -1.0;   // サービス終了時間
    	/*
    	          系内客数の処理
    	*/
    		c_sc_t += cus.size() * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    		sc_t    = p_time;   // 現在の系内客数になった時刻
    	/*
    	          滞在時間の処理
    	*/
    		Customer ct = cus.get(new Integer(sb[k]));
    		double x1 = p_time - ct.time;
    		c_sys += x1;   // 滞在時間の累計
    		if (x1 > m_sys)
    			m_sys = x1;   // 最大滞在時間
    		cus.remove(new Integer(sb[k]));   // 客の削除(系内客数)
    	/*
    	          窓口使用時間の処理
    	*/
    		asb     += 1;   // 空いている窓口数
    		sb[k]    = 0;   // 窓口kを空き状態にする
    		c_sb[k] += (p_time - st_s[k]);   // 窓口kがふさがっている時間の累計
    	/*
    	          待ち行列がある場合
    	*/
    		if (que.size() > 0) {
    			asb    -= 1;   // 開いている窓口数
    			c_lq_t += que.size() * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			int n  = que.pollFirst().intValue();   // 客の削除(待ち行列)
    			lq_t   = p_time;   // 現在の待ち行列長になった時刻
    			ct     = cus.get(new Integer(n));
    			x1     = p_time - ct.time;
    			c_wt  += x1;   // 待ち時間の累計
    			if (x1 > m_wt)
    				m_wt = x1;   // 最大待ち時間
    			k = -1;
    			for (int i1 = 0; i1 < s && k < 0; i1++) {
    				if (sb[i1] == 0) {
    					k        = i1;
    					sb[k]    = n;   // 窓口kの客番号
    					st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    					st_s[k]  = p_time;   // 窓口kがふさがった時刻
    					ct.state = k;   // 客の状態変更
    				}
    			}
    		}
    	/*
    	          待ち行列がない場合
    	*/
    		else {
    			if (asb == 1)
    				c_asb += (p_time - asb_t);   // すべての窓口がふさがっている時間の累計
    		}
    	}
    
    	/************************/
    	/* 結果の出力           */
    	/* (カッコ内は理論値) */
    	/************************/
    	void Output()
    	{
    		double rn  = (double)nc;
    		double rs  = (double)s;
    		double ram = 1.0 / m_a;
    		double myu = 1.0 / m_s;
    		double rou = ram / (rs * myu);
    		double p0, pi;
    		if (s == 1) {
    			p0 = 1.0 - rou;
    			pi = rou;
    		}
    		else {
    			p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)));
    			pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou));
    		}
    		double Lq = pi * rou / (1.0 - rou);
    		double L  = Lq + rs * rou;
    		double Wq = Lq / ram;
    		double W  = Wq + 1.0 / myu;
    		System.out.printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n",
    	           p_time, nc, rou, p0);
    		System.out.printf("   すべての窓口が塞がっている割合=%.3f (%.3f)\n",
    	           c_asb/p_time, pi);
    		System.out.printf("   各窓口が塞がっている割合\n");
    		for (int i1 = 0; i1 < s; i1++)
    			System.out.printf("      %d   %.3f\n", i1+1, c_sb[i1]/p_time);
    		System.out.printf("   平均待ち行列長=%.3f (%.3f)  最大待ち行列長=%d\n",
    	           c_lq_t/p_time, Lq, m_lq);
    		System.out.printf("   平均系内客数  =%.3f (%.3f)  最大系内客数  =%d\n",
    	           c_sc_t/p_time, L, m_sc);
    		System.out.printf("   平均待ち時間  =%.3f (%.3f)  最大待ち時間  =%.3f\n",
    	           c_wt/rn, Wq, m_wt);
    		System.out.printf("   平均滞在時間  =%.3f (%.3f)  最大滞在時間  =%.3f\n",
    	           c_sys/rn, W, m_sys);
    	}
    }
    
    /************************/
    /* クラスCustomerの定義 */
    /************************/
    class Customer
    {
    	double time;   // 到着時刻
    	int state;   // 客の状態
                             //   =-1 : 待ち行列に入っている
                             //   >=0 : サービスを受けている窓口番号
    	/*******************/
    	/* コンストラクタ  */
    	/*      n : 客番号 */
    	/*      s : 状態   */
    	/*******************/
    	Customer (int s, double t)
    	{
    		time  = t;
    		state = s;
    	}
    }
    			

  3. JavaScript

      ここをクリックすると,画面上でシミュレーションを実行し,結果を得ることができます.初期設定の状態は,上の図に示したモデルをシミュレーションするためのものです.

    test.html

    <!DOCTYPE HTML>
    
    <HTML>
    
    <HEAD>
    
    	<TITLE>簡単な待ち行列</TITLE>
    	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    	<SCRIPT TYPE="text/javascript" SRC="simple.js"></SCRIPT>
    
    </HEAD>
    
    <BODY STYLE="font-size: 130%; background-color: #eeffee;">
    
    	<H2 STYLE="text-align:center"><B>簡単な待ち行列</B></H2>
    
    	<DIV STYLE="text-align:center">
    		窓口の数:<INPUT ID="s" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="2"> 
    		シミュレーション時間:<INPUT ID="end" STYLE="font-size: 100%" TYPE="text" SIZE="5" VALUE="10000"><BR><BR>
    		平均到着時間間隔:<INPUT ID="m_a" STYLE="font-size: 100%" TYPE="text" SIZE="5" VALUE="5"> 
    		平均サービス時間:<INPUT ID="m_s" STYLE="font-size: 100%" TYPE="text" SIZE="5" VALUE="4"><BR><BR>
    		<BUTTON STYLE="font-size: 100%; background-color: pink" onClick="main()">実行</BUTTON> 結果は下<BR>
    		<TEXTAREA ID="res" COLS="60" ROWS="15" STYLE="font-size: 100%"></TEXTAREA>
    	</DIV>
    
    </BODY>
    
    </HTML>
    			

    simple.js

    /******************************/
    /* 簡単な待ち行列問題(M/M/s)*/
    /*      coded by Y.Suganuma   */
    /******************************/
    base = null;
    cus = new Array();
    que = new Array();
    
    /****************/
    /* main program */
    /****************/
    function main()
    {
    	cus.splice(0);   // 系内にいる客のリスト
    	que.splice(0);   // Queueオブジェクトリスト
    /*
              入力データ
    */
    	let s   = parseInt(document.getElementById("s").value);   // 窓口の数
    	let end = parseFloat(document.getElementById("end").value);   // シミュレーション終了時間
    	let m_a = parseFloat(document.getElementById("m_a").value);   // 平均到着時間間隔
    	let m_s = parseFloat(document.getElementById("m_s").value);   // 平均サービス時間
    /*
              システムの定義
    */
    	base = new Q_base(s, m_a, m_s, end);
    	base.at = base.p_time + base.Next_at();   // 最初の客の到着時間
    /*
              シミュレーションの実行
    */
    	base.Control();
    /*
              出力
    */
    	base.Output();
    }
    
    /******************************/
    /* Customerオブジェクトの定義 */
    /******************************/
    function Customer(n, s, t)
    {
    	this.time  = t;   // 到着時刻
    	this.num   = n;   // 客番号
    	this.state = s;   // 客の状態
    	                  //   =-1 : 待ち行列に入っている
    	                  //   >=0 : サービスを受けている窓口番号
    
    	return this;
    }
    
    /**********************/
    /* Q_baseオブジェクト */
    /**********************/
    function Q_base(s, m_a, m_s, end) {
    /*
              入力データの設定
    */
    	this.s = s;   // 窓口の数
    	this.m_a = m_a;   // 到着時間間隔の平均値
    	this.m_s = m_s;   // サービス時間の平均値
    	this.end = end;   // シミュレーション終了時刻
    /*
              初期設定
    */
    	this.asb = s;   // 全窓口の空き状況
    	                //    =0 : すべての窓口がふさがっている
    	                //    =n : n個の窓口が空いている
    	this.sb = new Array();   // 各窓口の空き状況
    	                         //    =0 : 窓口は空いている
    	                         //    >0 : サービスを受けている客番号
    	this.c_sb = new Array();   // 各窓口がふさがっている時間の累計
    	this.st_e = new Array();   // 各窓口のサービス終了時刻
    	this.st_s = new Array();   // 各窓口がふさがった時刻
    	for (let i1 = 0; i1 < this.s; i1++) {
    		this.sb[i1]   = 0;
    		this.c_sb[i1] = 0.0;
    	}
    	this.asb_t;   // すべての窓口がふさがった時刻
    	this.c_asb = 0.0;   // すべての窓口がふさがっている時間の累計
    	this.m_lq = 0;   // 最大待ち行列長
    	this.c_lq_t = 0.0;   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    	this.c_wt = 0.0;   // 待ち時間の累計
    	this.lq_t = 0.0;   // 現在の待ち行列長になった時刻
    	this.m_wt = 0.0;   // 最大待ち時間
    	this.c_sc_t = 0.0;   // 系内客数にその数が継続した時間を乗じた値の累計
    	this.c_sys = 0.0;   // 滞在時間の累計
    	this.m_sys = 0.0;   // 最大滞在時間
    	this.sc_t = 0.0;   // 現在の系内客数になった時刻
    	this.m_sc = 0;   // 最大系内客数
    	this.at;   // 次の客の到着時刻(負の時は,終了)
    	this.p_time = 0.0;   // 現在時刻
    	this.nc = 0;   // 到着客数カウンタ
    
    	return this;
    }
    
    /********************************/
    /* 次の客の到着までの時間の発生 */
    /********************************/
    Q_base.prototype.Next_at = function()
    {
    	return -base.m_a * Math.log(Math.random());
    }
    
    /**********************/
    /* サービス時間の発生 */
    /**********************/
    Q_base.prototype.Next_sv = function()
    {
    	return -base.m_s * Math.log(Math.random());
    }
    
    /**************/
    /* 全体の制御 */
    /**************/
    Q_base.prototype.Control = function()
    {
    	let sw = 0;
    	while (sw > -2) {
    		sw = base.Next();   // 次の処理の選択
    		if (sw == -1)
    			sw = base.End_o_s();   // シミュレーションの終了
    		else {
    			if (sw == 0)
    				base.Arrive();   // 客の到着処理
    			else
    				base.Service(sw);   // サービスの終了
    		}
    	}
    }
    
    /**************************************************/
    /* 次の処理の決定                                 */
    /*      return : =-1 : シミュレーションの終了     */
    /*               =0  : 客の到着処理               */
    /*               =i  : i番目の窓口のサービス終了 */
    /**************************************************/
    Q_base.prototype.Next = function()
    {
    	let sw = -1;
    	let t  = base.end;   // シミュレーション終了時刻で初期設定
    					// 次の客の到着時刻
    	if (base.at >= 0.0 && base.at < t) {
    		sw = 0;
    		t  = base.at;
    	}
    					// サービス終了時刻
    	for (let i1 = 0; i1 < base.s; i1++) {
    		if (base.sb[i1] > 0 && base.st_e[i1] <= t) {
    			sw = i1 + 1;
    			t  = base.st_e[i1];   // 窓口i1のサービス終了時刻
    		}
    	}
    
    	return sw;
    }
    
    /**********************************/
    /* 終了処理                       */
    /*      return : =-1 : 終了前処理 */
    /*               =-2 : 実際の終了 */
    /**********************************/
    Q_base.prototype.End_o_s = function()
    {
    	let sw      = -2;
    	base.p_time = base.end;   // 現在時刻
    	base.at     = -1.0;   // 次の客の到着時刻
    
    	for (let i1 = 0; i1 < base.s; i1++) {
    		if (base.sb[i1] > 0) {   // サービス中の客はいるか?
    			if (sw == -2) {
    				sw = -1;
    				base.end = base.st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    			else {
    				if (base.st_e[i1] > base.end)
    					base.end = base.st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    		}
    	}
    
    	return sw;
    }
    
    /****************/
    /* 客の到着処理 */
    /****************/
    Q_base.prototype.Arrive = function()
    {
    /*
              客数の増加と次の客の到着時刻の設定
    */
    	base.nc     += 1;   // 到着客数カウンタ
    	base.p_time  = base.at;   // 現在時刻
    	base.at      = base.p_time + base.Next_at();   // 次の客の到着時刻
    	if (base.at >= base.end)
    		base.at = -1.0;
    /*
              系内客数の処理
    */
    	base.c_sc_t += cus.length * (base.p_time - base.sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    	base.sc_t    = base.p_time;   // 現在の系内客数になった時刻
    	if (cus.length+1 > base.m_sc)
    		base.m_sc = cus.length + 1;   // 最大系内客数
    /*
              窓口に空きがない場合
    */
    	if (base.asb == 0) {
    		let ct = new Customer(base.nc, -1, base.p_time);
    		cus.push(ct);   // 客の登録(系内客数)
    		base.c_lq_t += que.length * (base.p_time - base.lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    		que.push(base.nc);   // 客の登録(待ち行列)
    		base.lq_t = base.p_time;   // 現在の待ち行列長になった時刻
    		if (que.length > base.m_lq)
    			base.m_lq = que.length;   // 最大待ち行列長
    	}
    /*
              すぐサービスを受けられる場合
    */
    	else {
    		let k = -1;
    		for (let i1 = 0; i1 < base.s && k < 0; i1++) {
    			if (base.sb[i1] == 0) {
    				let ct = new Customer(base.nc, i1, base.p_time);
    				cus.push(ct);   // 客の登録(系内客数)
    				k = i1;
    				base.sb[k]    = base.nc;   // サービスを受けている客番号
    				base.st_e[k]  = base.p_time + base.Next_sv();   // 窓口kのサービス終了時刻
    				base.asb     -= 1;   // 空いている窓口数
    			}
    		}
    		base.st_s[k] = base.p_time;   // 窓口kがふさがった時刻
    		if (base.asb == 0)
    			base.asb_t = base.p_time;   // すべての窓口がふさがった時刻
    	}
    }
    
    /*********************************/
    /* サービス終了時の処理          */
    /*      k : サービス終了窓口番号 */
    /*********************************/
    Q_base.prototype.Service = function(k)
    {
    /*
              時間の設定
    */
    	k -= 1;
    	base.p_time  = base.st_e[k];   // 現在時刻
    	base.st_e[k] = -1.0;   // サービス終了時間
    /*
              系内客数の処理
    */
    	base.c_sc_t += cus.length * (base.p_time - base.sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    	base.sc_t    = base.p_time;   // 現在の系内客数になった時刻
    /*
              滞在時間の処理
    */
    	let n  = base.Search(base.sb[k]);
    	let x1 = base.p_time - cus[n].time;
    	base.c_sys += x1;   // 滞在時間の累計
    	if (x1 > base.m_sys)
    		base.m_sys = x1;   // 最大滞在時間
    	cus.splice(n, 1);   // 客の削除(系内客数)
    /*
              窓口使用時間の処理
    */
    	base.asb     += 1;   // 空いている窓口数
    	base.sb[k]    = 0;   // 窓口kを空き状態にする
    	base.c_sb[k] += (base.p_time - base.st_s[k]);   // 窓口kがふさがっている時間の累計
    /*
              待ち行列がある場合
    */
    	if (que.length > 0) {
    		base.asb    -= 1;   // 開いている窓口数
    		base.c_lq_t += que.length * (base.p_time - base.lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    		n = que.shift();   // 客の削除(待ち行列)
    		base.lq_t = base.p_time;   // 現在の待ち行列長になった時刻
    		let m  = base.Search(n);
    		x1 = base.p_time - cus[m].time;
    		base.c_wt += x1;   // 待ち時間の累計
    		if (x1 > base.m_wt)
    			base.m_wt = x1;   // 最大待ち時間
    		let k = -1;
    		for (let i1 = 0; i1 < base.s && k < 0; i1++) {
    			if (base.sb[i1] == 0) {
    				k = i1;
    				base.sb[k]   = n;   // 窓口kの客番号
    				base.st_e[k] = base.p_time + base.Next_sv();   // 窓口kのサービス終了時刻
    				base.st_s[k] = base.p_time;   // 窓口kがふさがった時刻
    				cus[m].state = k;   // 客の状態変更
    			}
    		}
    	}
    /*
              待ち行列がない場合
    */
    	else {
    		if (base.asb == 1)
    			base.c_asb += (base.p_time - base.asb_t);   // すべての窓口がふさがっている時間の累計
    	}
    }
    
    /****************************/
    /* 指定した客の配列上の位置 */
    /*      nm : 客番号         */
    /*      return : 要素番号   */
    /****************************/
    Q_base.prototype.Search = function(nm)
    {
    	let n = -1;
    
    	for (let i1 = 0; i1 < cus.length; i1++) {
    		if (cus[i1].num == nm) {
    			n = i1;
    			break;
    		}
    	}
    
    	return n;
    }
    
    /************************/
    /* 結果の出力           */
    /* (カッコ内は理論値) */
    /************************/
    Q_base.prototype.Output = function()
    {
    	let ram = 1.0 / base.m_a;
    	let myu = 1.0 / base.m_s;
    	let rou = ram / (base.s * myu);
    	let pi, p0;
    	if (base.s == 1) {
    		p0 = 1.0 - rou;
    		pi = rou;
    	}
    	else {
    		p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)));
    		pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou));
    	}
    	let Lq = pi * rou / (1.0 - rou);
    	let L  = Lq + base.s * rou;
    	let Wq = Lq / ram;
    	let W  = Wq + 1.0 / myu;
    	let str = "シミュレーション時間=" + Math.round(base.p_time) + " 客数=" + base.nc + " ρ=" + Math.round(1000*rou)/1000 + " p0=" + Math.round(1000*p0)/1000 + "\n";
    	str += "   すべての窓口が塞がっている割合=" + Math.round(1000*base.c_asb/base.p_time)/1000 + " (" + Math.round(1000*pi)/1000 + ")\n";
    	str += "   各窓口が塞がっている割合\n";
    	for (let i1 = 0; i1 < base.s; i1++)
    		str += "      " + (i1+1) + "   " + Math.round(1000*base.c_sb[i1]/base.p_time)/1000 + "\n";
    	str += "   平均待ち行列長=" + Math.round(1000*base.c_lq_t/base.p_time)/1000 + " (" + Math.round(1000*Lq)/1000 + ")  最大待ち行列長=" + base.m_lq + "\n";
    	str += "   平均系内客数  =" + Math.round(1000*base.c_sc_t/base.p_time)/1000 + " (" + Math.round(1000*L)/1000 + ")  最大系内客数  =" + base.m_sc + "\n";
    	str += "   平均待ち時間  =" + Math.round(1000*base.c_wt/base.nc)/1000 + " (" + Math.round(1000*Wq)/1000 + ")  最大待ち時間  =" + Math.round(base.m_wt) + "\n";
    	str += "   平均滞在時間  =" + Math.round(1000*base.c_sys/base.nc)/1000 + " (" + Math.round(1000*W)/1000 + ")  最大滞在時間  =" + Math.round(base.m_sys) + "\n";
    
    	document.getElementById("res").value = str;
    }
    			

  4. PHP

    <?php
    
    /******************************/
    /* 簡単な待ち行列問題(M/M/s)*/
    /*      coded by Y.Suganuma   */
    /******************************/
    
    /************************/
    /* クラスCustomerの定義 */
    /************************/
    class Customer
    {
    	public $time;   // 到着時刻
    	public $state;   // 客の状態
                         //   =-1 : 待ち行列に入っている
                         //   >=0 : サービスを受けている窓口番号
    	/*********************/
    	/* コンストラクタ    */
    	/*      s : 状態     */
    	/*      t : 到着時刻 */
    	/*******************************/
    	function Customer($s, $t)
    	{
    		$this->time  = $t;
    		$this->state = $s;
    	}
    }
    
    /**********************/
    /* クラスQ_baseの定義 */
    /**********************/
    class Q_base {
    	private $s;   // 窓口の数
    	private $asb;   // 全窓口の空き状況
                        //    =0 : すべての窓口がふさがっている
                        //    =n : n個の窓口が空いている
    	private $sb;   // 各窓口の空き状況
                       //    =0 : 窓口は空いている
                       //    >0 : サービスを受けている客番号
    	private $asb_t;   // すべての窓口がふさがった時刻
    	private $c_asb;   // すべての窓口がふさがっている時間の累計
    	private $c_sb;   // 各窓口がふさがっている時間の累計
    	private $st_e;   // 各窓口のサービス終了時刻
    	private $st_s;   // 各窓口がふさがった時刻
    	private $m_lq;   // 最大待ち行列長
    	private $c_lq_t;   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    	private $c_wt;   // 待ち時間の累計
    	private $lq_t;   // 現在の待ち行列長になった時刻
    	private $m_wt;   // 最大待ち時間
    	private $c_sc_t;   // 系内客数にその数が継続した時間を乗じた値の累計
    	private $c_sys;   // 滞在時間の累計
    	private $m_sys;   // 最大滞在時間
    	private $sc_t;   // 現在の系内客数になった時刻
    	private $m_sc;   // 最大系内客数
    	private $m_a;   // 到着時間間隔の平均値
    	private $m_s;   // サービス時間の平均値
    	private $at;   // 次の客の到着時刻(負の時は,終了)
    	private $p_time;   // 現在時刻
    	private $end;   // シミュレーション終了時刻
    	private $nc;   // 到着客数カウンタ
    	private $cus;   // 系内にいる客のリスト
    	private $que;   // 待ち行列
    
    	/*****************************************/
    	/* コンストラクタ                        */
        /*      s_i : 窓口の数                   */
        /*      m_a_i : 到着時間間隔の平均値     */
    	/*      m_s_i : サービス時間の平均値     */
        /*      end_i : シミュレーション終了時刻 */
        /*****************************************/
    	function Q_base ($s_i, $m_a_i, $m_s_i, $end_i)
    	{
    	/*
    	          設定
    	*/
    		$this->s   = $s_i;
    		$this->m_a = $m_a_i;
    		$this->m_s = $m_s_i;
    		$this->end = $end_i;
    	/*
    	          領域の確保
    	*/
    		$this->sb   = array();
    		$this->c_sb = array();
    		$this->st_e = array();
    		$this->st_s = array();
    		$this->cus  = array();
    		$this->que  = array();
    	
    		for ($i1 = 0; $i1 < $this->s; $i1++) {
    			$this->sb[$i1]   = 0;
    			$this->c_sb[$i1] = 0.0;
    		}
    	/*
    	          初期設定
    	*/
    		$this->p_time = 0.0;
        	$this->nc     = 0;
        	$this->asb    = $this->s;
    		$this->m_lq   = 0;
        	$this->m_sc   = 0;
        	$this->c_asb  = 0.0;
    		$this->c_wt   = 0.0;
    		$this->m_wt   = 0.0;
    		$this->c_lq_t = 0.0;
    		$this->lq_t   = 0.0;
    		$this->m_sys  = 0.0;
    		$this->c_sys  = 0.0;
    		$this->c_sc_t = 0.0;
    		$this->sc_t   = 0.0;
    	/*
    	          乱数の初期設定
    	*/
    		mt_srand();
    	/*
    	          最初の客の到着時刻の設定
    	*/
    		$this->at = $this->p_time + $this->Next_at();
    	}
    	
    	/********************************/
    	/* 次の客の到着までの時間の発生 */
    	/********************************/
    	function Next_at()
    	{
    		return -$this->m_a * log(mt_rand() / mt_getrandmax());
    	}
        
        /************************/
    	/* サービス時間の発生   */
        /************************/
        function Next_sv()
    	{
    		return -$this->m_s * log(mt_rand() / mt_getrandmax());
    	}
    	
    	/**************/
    	/* 全体の制御 */
    	/**************/
    	function Control()
    	{
    		$sw = 0;
    		while ($sw > -2) {
    			$sw = $this->Next();   // 次の処理の選択
    			if ($sw == -1)
    				$sw = $this->End_o_s();   // シミュレーションの終了
    			else {
    				if ($sw == 0)
    					$this->Arrive();   // 客の到着処理
    				else
    					$this->Service($sw);   // サービスの終了
    			}
    		}
    	}
    	
    	/**************************************************/
    	/* 次の処理の決定                                 */
        /*      return : =-1 : シミュレーションの終了     */
        /*               =0  : 客の到着処理               */
    	/*               =i  : i番目の窓口のサービス終了 */
        /**************************************************/
        function Next()
    	{
    		$sw = -1;
    		$t  = $this->end;   // シミュレーション終了時刻で初期設定
    						// 次の客の到着時刻
    		if ($this->at >= 0.0 && $this->at < $t) {
    			$sw = 0;
    			$t  = $this->at;
    		}
    						// サービス終了時刻
    		for ($i1 = 0; $i1 < $this->s; $i1++) {
    			if ($this->sb[$i1] > 0 && $this->st_e[$i1] <= $t) {
    				$sw = $i1 + 1;
    				$t  = $this->st_e[$i1];   // 窓口i1のサービス終了時刻
    			}
    		}
    	
    		return $sw;
    	}
    	
    	/**********************************/
    	/* 終了処理                       */
    	/*      return : =-1 : 終了前処理 */
    	/*               =-2 : 実際の終了 */
    	/**********************************/
    	function End_o_s()
        {
        	$sw           = -2;
    		$this->p_time = $this->end;   // 現在時刻
        	$this->at     = -1.0;   // 次の客の到着時刻
        
    		for ($i1 = 0; $i1 < $this->s; $i1++) {
    			if ($this->sb[$i1] > 0) {   // サービス中の客はいるか?
    				if ($sw == -2) {
    					$sw  = -1;
    					$this->end = $this->st_e[$i1];   // 窓口i1のサービス終了時刻
    				}
    				else {
    					if ($this->st_e[$i1] > $this->end)
    						$this->end = $this->st_e[$i1];   // 窓口i1のサービス終了時刻
    				}
    			}
    		}
    	
    		return $sw;
    	}
    	
    	/****************/
    	/* 客の到着処理 */
    	/****************/
    	function Arrive()
    	{
    	/*
    	          客数の増加と次の客の到着時刻の設定
    	*/
    		$this->nc     += 1;   // 到着客数カウンタ
        	$this->p_time  = $this->at;   // 現在時刻
        	$this->at      = $this->p_time + $this->Next_at();      // 次の客の到着時刻
    		if ($this->at >= $this->end)
        		$this->at = -1.0;
        /*
    	          系内客数の処理
    	*/
    		$this->c_sc_t += (count($this->cus) * ($this->p_time - $this->sc_t));   // 系内客数にその数が継続した時間を乗じた値の累計
    		$this->sc_t    = $this->p_time;   // 現在の系内客数になった時刻
    		if (count($this->cus)+1 > $this->m_sc)
    			$this->m_sc = count($this->cus) + 1;   // 最大系内客数
    	/*
    	          窓口に空きがない場合
    	*/
    		if ($this->asb == 0) {
    			$ct_p = new Customer(-1, $this->p_time);
    			$this->cus['no'.strval($this->nc)] = $ct_p;   // 客の登録(系内客数)
    			$this->c_lq_t += count($this->que) * ($this->p_time - $this->lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			array_push($this->que, $this->nc);   // 客の登録(待ち行列)
    			$this->lq_t = $this->p_time;   // 現在の待ち行列長になった時刻
    			if (count($this->que) > $this->m_lq)
    				$this->m_lq = count($this->que);   // 最大待ち行列長
    		}
    	/*
    	          すぐサービスを受けられる場合
    	*/
    		else {
    			$k = -1;
    			for ($i1 = 0; $i1 < $this->s && $k < 0; $i1++) {
    				if ($this->sb[$i1] == 0) {
        				$ct_p = new Customer($i1, $this->p_time);
        				$this->cus['no'.strval($this->nc)] = $ct_p;   // 客の登録(系内客数)
    					$k              = $i1;
        				$this->sb[$k]   = $this->nc;   // サービスを受けている客番号
        				$this->st_e[$k] = $this->p_time + $this->Next_sv();   // 窓口kのサービス終了時刻
    					$this->asb     -= 1;   // 空いている窓口数
    				}
    			}
    			$this->st_s[$k] = $this->p_time;   // 窓口kがふさがった時刻
    			if ($this->asb == 0)
    				$this->asb_t = $this->p_time;   // すべての窓口がふさがった時刻
    		}
    	}
    	
    	/*********************************/
    	/* サービス終了時の処理          */
    	/*      k : サービス終了窓口番号 */
    	/*********************************/
    	function Service($k)
    	{
    	/*
    	          時間の設定
    	*/
    		$k             -= 1;
    		$this->p_time   = $this->st_e[$k];   // 現在時刻
    		$this->st_e[$k] = -1.0;   // サービス終了時間
    	/*
    	          系内客数の処理
    	*/
    		$this->c_sc_t += (count($this->cus) * ($this->p_time - $this->sc_t));   // 系内客数にその数が継続した時間を乗じた値の累計
        	$this->sc_t    = $this->p_time;   // 現在の系内客数になった時刻
        /*
    	          滞在時間の処理
        */
        	$it = $this->cus['no'.strval($this->sb[$k])];
    		$x1 = $this->p_time - $it->time;
    		$this->c_sys += $x1;   // 滞在時間の累計
    		if ($x1 > $this->m_sys)
    			$this->m_sys = $x1;   // 最大滞在時間
    		unset($this->cus['no'.strval($this->sb[$k])]);   // 客の削除(系内客数)
    	/*
    	          窓口使用時間の処理
    	*/
    		$this->asb      += 1;   // 空いている窓口数
    		$this->sb[$k]    = 0;   // 窓口kを空き状態にする
    		$this->c_sb[$k] += ($this->p_time - $this->st_s[$k]);   // 窓口kがふさがっている時間の累計
    	/*
    	          待ち行列がある場合
    	*/
    		if (count($this->que) > 0) {
    			$this->asb    -= 1;   // 開いている窓口数
    			$this->c_lq_t += count($this->que) * ($this->p_time - $this->lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			$n = array_shift($this->que);   // 客の削除(待ち行列)
    			$this->lq_t  = $this->p_time;   // 現在の待ち行列長になった時刻
    			$it          = $this->cus['no'.strval($n)];
    			$x1          = $this->p_time - $it->time;
    			$this->c_wt += $x1;   // 待ち時間の累計
    			if ($x1 > $this->m_wt)
    				$this->m_wt = $x1;   // 最大待ち時間
        		$k = -1;
        		for ($i1 = 0; $i1 < $this->s && $k < 0; $i1++) {
    				if ($this->sb[$i1] == 0) {
        				$k              = $i1;
        				$this->sb[$k]   = $n;   // 窓口kの客番号
    					$this->st_e[$k] = $this->p_time + $this->Next_sv();   // 窓口kのサービス終了時刻
    					$this->st_s[$k] = $this->p_time;   // 窓口kがふさがった時刻
    					$it->state      = $k;   // 客の状態変更
    				}
    			}
    		}
    	/*
    	          待ち行列がない場合
    	*/
    		else {
    			if ($this->asb == 1)
    				$this->c_asb += ($this->p_time - $this->asb_t);   // すべての窓口がふさがっている時間の累計
    		}
    	}
    	
    	/************************/
    	/* 結果の出力           */
    	/* (カッコ内は理論値) */
    	/************************/
    	function Output()
    	{
    		$rn  = floatval($this->nc);
    		$rs  = floatval($this->s);
    		$ram = 1.0 / $this->m_a;
    		$myu = 1.0 / $this->m_s;
        	$rou = $ram / ($rs * $myu);
    		if ($this->s == 1) {
        		$p0 = 1.0 - $rou;
        		$pi = $rou;
    		}
    		else {
    			$p0 = 1.0 / (1.0 + 2.0 * $rou + 4.0 * $rou * $rou / (2.0 * (1.0 - $rou)));
    			$pi = 4.0 * $rou * $rou * $p0 / (2.0 * (1.0 - $rou));
    		}
    		$Lq = $pi * $rou / (1.0 - $rou);
    		$L  = $Lq + $rs * $rou;
    		$Wq = $Lq / $ram;
    		$W  = $Wq + 1.0 / $myu;
    		printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n",
    	           $this->p_time, $this->nc, $rou, $p0);
    		printf("   すべての窓口が塞がっている割合=%.3f (%.3f)\n",
    	           $this->c_asb/$this->p_time, $pi);
    		printf("   各窓口が塞がっている割合\n");
    		for ($i1 = 0; $i1 < $this->s; $i1++)
    			printf("      %d   %.3f\n", $i1+1, $this->c_sb[$i1]/$this->p_time);
    		printf("   平均待ち行列長=%.3f (%.3f)  最大待ち行列長=%d\n",
    	           $this->c_lq_t/$this->p_time, $Lq, $this->m_lq);
    		printf("   平均系内客数  =%.3f (%.3f)  最大系内客数  =%d\n",
    	           $this->c_sc_t/$this->p_time, $L, $this->m_sc);
    		printf("   平均待ち時間  =%.3f (%.3f)  最大待ち時間  =%.3f\n",
    	           $this->c_wt/$rn, $Wq, $this->m_wt);
    		printf("   平均滞在時間  =%.3f (%.3f)  最大滞在時間  =%.3f\n",
    	           $this->c_sys/$rn, $W, $this->m_sys);
    	}
    }
        
    /****************/
    /* main program */
    /****************/
    /*
              入力データ
    */
    	printf("窓口の数は? ");
    	fscanf(STDIN, "%d", $s);
    
    	printf("シミュレーション終了時間? ");
    	fscanf(STDIN, "%lf", $end);
    
    	printf("   到着時間間隔の平均値は? ");
    	fscanf(STDIN, "%lf", $m_a);
    
    	printf("   サービス時間の平均値は? ");
    	fscanf(STDIN, "%lf", $m_s);
    /*
              システムの定義
    */
    	$base = new Q_base($s, $m_a, $m_s, $end);
    /*
              シミュレーションの実行
    */
    	$base->Control();
    /*
             出力
    */
    	$base->Output();
    
    ?>
    			

  5. Ruby

    #############################
    # 簡単な待ち行列問題(M/M/s)
    #      coded by Y.Suganuma
    #############################
    
    ########################
    # クラスCustomerの定義
    ########################
    
    class Customer
    
    	#####################
    	# コンストラクタ
    	#      s : 状態
    	#      t : 到着時刻
    	#####################
    	def initialize(s, t)
    		@_state = s   # 客の状態
    	                  #   =-1 : 待ち行列に入っている
    	                  #   >=0 : サービスを受けている窓口番号
    		@_time  = t   # 到着時刻
    	end
    	attr_accessor("_state", "_time")
    end
    
    ########################
    # クラスQ_baseの定義
    ########################
    
    class Q_base
    
    	#########################################
    	# コンストラクタ
    	#      s_i : 窓口の数
    	#      m_a_i : 到着時間間隔の平均値
    	#      m_s_i : サービス時間の平均値
    	#      stp_i : シミュレーション終了時刻
    	#########################################
    
    	def initialize(s_i, m_a_i, m_s_i, stp_i)
    				# 設定
    		@_s   = s_i   # 窓口の数
    		@_m_a = m_a_i   # 到着時間間隔の平均値
    		@_m_s = m_s_i   # サービス時間の平均値
    		@_stp = stp_i   # シミュレーション終了時刻
    				# 領域の確保
    		@_sb   = Array.new(@_s)   # 各窓口の空き状況
    		                          #    =0 : 窓口は空いている
    		                          #    >0 : サービスを受けている客番号
    		@_c_sb = Array.new(@_s)   # 各窓口がふさがっている時間の累計
    		for i1 in 0 ... @_s
    			@_sb[i1]   = 0
    			@_c_sb[i1] = 0.0
    		end
    		@_st_e = Array.new(@_s)   # 各窓口のサービス終了時刻
    		@_st_s = Array.new(@_s)   # 各窓口がふさがった時刻
    				# 初期設定
    		@_p_time = 0.0  # 現在時刻
    		@_nc     = 0   # 到着客数カウンタ
    		@_asb    = @_s   # 全窓口の空き状況
    		                 #    =0 : すべての窓口がふさがっている
    		                 #    =n : n個の窓口が空いている
    		@_m_lq   = 0   # 最大待ち行列長
    		@_m_sc   = 0   # 最大系内客数
    		@_c_asb  = 0.0   # すべての窓口がふさがっている時間の累計
    		@_c_wt   = 0.0   # 待ち時間の累計
    		@_m_wt   = 0.0   # 最大待ち時間
    		@_c_lq_t = 0.0   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    		@_lq_t   = 0.0   # 現在の待ち行列長になった時刻
    		@_m_sys  = 0.0   # 最大滞在時間
    		@_c_sys  = 0.0   # 滞在時間の累計
    		@_c_sc_t = 0.0   # 系内客数にその数が継続した時間を乗じた値の累計
    		@_sc_t   = 0.0   # 現在の系内客数になった時刻
    		@_asb_t  = 0.0   # すべての窓口がふさがった時刻
    		@_cus    = Hash.new()   # 系内にいる客のリスト
    		@_que    = Array.new()   # 待ち行列
    			# 最初の客の到着時刻の設定
    		@_at = @_p_time + Next_at()   # 次の客の到着時刻(負の時は,終了)
    	end
    	
    	################################
    	# 次の客の到着までの時間の発生
    	################################
    
    	def Next_at()
    		return -@_m_a * Math.log(rand(0))
    	end
    	
    	#####################
    	# サービス時間の発生
    	#####################
    
    	def Next_sv()
    		return -@_m_s * Math.log(rand(0))
    	end
    	
    	##############
    	# 全体の制御
    	##############
    
    	def Control()
    	
    		sw = 0
    		while sw > -2
    			sw = Next()   # 次の処理の選択
    			if sw == -1
    				sw = End_o_s()   # シミュレーションの終了
    			else
    				if sw == 0
    					Arrive()   # 客の到着処理
    				else
    					Service(sw)   # サービスの終了
    				end
    			end
    		end
    	end
    	
    	##################################################
    	# 次の処理の決定
    	#      return : =-1 : シミュレーションの終了
    	#               =0  : 客の到着処理
    	#               =i  : i番目の窓口のサービス終了
    	##################################################
    
    	def Next()
    	
    		sw = -1
    		t  = @_stp   # シミュレーション終了時刻で初期設定
    						# 次の客の到着時刻
    		if @_at >= 0.0 && @_at < t
    			sw = 0
    			t  = @_at
    		end
    						# サービス終了時刻
    		for i1 in 0 ... @_s
    			if @_sb[i1] > 0 && @_st_e[i1] <= t
    				sw = i1 + 1
    				t  = @_st_e[i1]   # 窓口i1のサービス終了時刻
    			end
    		end
    	
    		return sw
    	end
    	
    	##################################
    	# 終了処理
    	#      return : =-1 : 終了前処理
    	#               =-2 : 実際の終了
    	##################################
    
    	def End_o_s()
    	
    		sw       = -2
    		@_p_time = @_stp   # 現在時刻
    		@_at     = -1.0   # 次の客の到着時刻
    	
    		for i1 in 0 ... @_s
    			if @_sb[i1] > 0   # サービス中の客はいるか?
    				if sw == -2
    					sw    = -1
    					@_stp = @_st_e[i1]   # 窓口i1のサービス終了時刻
    				else
    					if @_st_e[i1] > @_stp
    						@_stp = @_st_e[i1]   # 窓口i1のサービス終了時刻
    					end
    				end
    			end
    		end
    	
    		return sw
    	end
    	
    	################
    	# 客の到着処理
    	################
    
    	def Arrive()
    				# 客数の増加と次の客の到着時刻の設定
    		@_nc     += 1   # 到着客数カウンタ
    		@_p_time  = @_at   # 現在時刻
    		@_at      = @_p_time + Next_at()   # 次の客の到着時刻
    		if @_at >= @_stp
    			@_at = -1.0
    		end
    				# 系内客数の処理
    		@_c_sc_t += @_cus.length * (@_p_time - @_sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
    		@_sc_t    = @_p_time   # 現在の系内客数になった時刻
    		if @_cus.length+1 > @_m_sc
    			@_m_sc = @_cus.length + 1   # 最大系内客数
    		end
    				# 窓口に空きがない場合
    		if @_asb == 0
    			ct_p         = Customer.new(-1, @_p_time)
    			@_cus[@_nc]  = ct_p   # 客の登録(系内客数)
    			@_c_lq_t    += @_que.length * (@_p_time - @_lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    			@_que.push(@_nc)   # 客の登録(待ち行列)
    			@_lq_t = @_p_time   # 現在の待ち行列長になった時刻
    			if @_que.length > @_m_lq
    				@_m_lq = @_que.length   # 最大待ち行列長
    			end
    				# すぐサービスを受けられる場合
    		else
    			k = -1
    			for i1 in 0 ... @_s
    				if @_sb[i1] == 0
    					ct_p         = Customer.new(i1, @_p_time)
    					@_cus[@_nc]  = ct_p   # 客の登録(系内客数)
    					k            = i1
    					@_sb[k]      = @_nc   # サービスを受けている客番号
    					@_st_e[k]    = @_p_time + Next_sv()   # 窓口kのサービス終了時刻
    					@_asb       -= 1   # 空いている窓口数
    					break
    				end
    			end
    			@_st_s[k] = @_p_time   # 窓口kがふさがった時刻
    			if @_asb == 0
    				@_asb_t = @_p_time   # すべての窓口がふさがった時刻
    			end
    		end
    	end
    	
    	#################################
    	# サービス終了時の処理
    	#      k : サービス終了窓口番号
    	#################################
    
    	def Service(k)
    				# 時間の設定
    		k         -= 1
    		@_p_time   = @_st_e[k]   # 現在時刻
    		@_st_e[k]  = -1.0   # サービス終了時間
    				# 系内客数の処理
    		@_c_sc_t  += @_cus.length * (@_p_time - @_sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
    		@_sc_t     = @_p_time   # 現在の系内客数になった時刻
    				# 滞在時間の処理
    		x1         = @_p_time - @_cus[@_sb[k]]._time
    		@_c_sys   += x1   # 滞在時間の累計
    		if x1 > @_m_sys
    			@_m_sys = x1   # 最大滞在時間
    		end
    		@_cus.delete(@_sb[k])   # 客の削除(系内客数)
    				# 窓口使用時間の処理
    		@_asb     += 1   # 空いている窓口数
    		@_sb[k]    = 0   # 窓口kを空き状態にする
    		@_c_sb[k] += (@_p_time - @_st_s[k])   # 窓口kがふさがっている時間の累計
    				# 待ち行列がある場合
    		if @_que.length > 0
    			@_asb    -= 1   # 開いている窓口数
    			@_c_lq_t += @_que.length * (@_p_time - @_lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    			n         = @_que.delete_at(0)
    			@_lq_t    = @_p_time   # 現在の待ち行列長になった時刻
    			x1        = @_p_time - @_cus[n]._time
    			@_c_wt   += x1   # 待ち時間の累計
    			if x1 > @_m_wt
    				@_m_wt = x1   # 最大待ち時間
    			end
    			k = -1
    			for i1 in 0 ... @_s
    				if @_sb[i1] == 0
    					k               = i1
    					@_sb[k]         = n   # 窓口kの客番号
    					@_st_e[k]       = @_p_time + Next_sv()   # 窓口kのサービス終了時刻
    					@_st_s[k]       = @_p_time   # 窓口kがふさがった時刻
    					@_cus[n]._state = k   # 客の状態変更
    					break
    				end
    			end
    				# 待ち行列がない場合
    		else
    			if @_asb == 1
    				@_c_asb += (@_p_time - @_asb_t)   # すべての窓口がふさがっている時間の累計
    			end
    		end
    	end
    	
    	########################
    	# 結果の出力
    	# (カッコ内は理論値)
    	########################
    
    	def Output()
    	
    		rn  = Float(@_nc)
    		rs  = Float(@_s)
    		ram = 1.0 / @_m_a
    		myu = 1.0 / @_m_s
    		rou = ram / (rs * myu)
    		if @_s == 1
    			p0 = 1.0 - rou
    			pi = rou
    		else
    			p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)))
    			pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou))
    		end
    		lq = pi * rou / (1.0 - rou)
    		l  = lq + rs * rou
    		wq = lq / ram
    		w  = wq + 1.0 / myu
    		printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n", @_p_time, @_nc, rou, p0)
    		printf("   すべての窓口が塞がっている割合=%.3f (%.3f)\n", @_c_asb/@_p_time, pi)
    		printf("   各窓口が塞がっている割合\n")
    		for i1 in 0 ... @_s
    			printf("      %d   %.3f\n", (i1+1), @_c_sb[i1]/@_p_time)
    		end
    		printf("   平均待ち行列長=%.3f (%.3f)  最大待ち行列長=%d\n", @_c_lq_t/@_p_time, lq, @_m_lq)
    		printf("   平均系内客数  =%.3f (%.3f)  最大系内客数  =%d\n", @_c_sc_t/@_p_time, l, @_m_sc)
    		printf("   平均待ち時間  =%.3f (%.3f)  最大待ち時間  =%.3f\n", @_c_wt/rn, wq, @_m_wt)
    		printf("   平均滞在時間  =%.3f (%.3f)  最大滞在時間  =%.3f\n", @_c_sys/rn, w, @_m_sys)
    	end
    end
    
    			# 入力データ
    print("窓口の数は? ")
    s = Integer(gets())
    print("シミュレーション終了時間? ")
    stp = Float(gets())
    print("   到着時間間隔の平均値は? ")
    m_a = Float(gets())
    print("   サービス時間の平均値は? ")
    m_s = Float(gets())
    			# システムの定義
    base = Q_base.new(s, m_a, m_s, stp)
    			# シミュレーションの実行
    srand()
    base.Control()
    			# 出力
    base.Output()
    			

  6. Python

    # -*- coding: UTF-8 -*-
    import numpy as np
    import sys
    from math import *
    from random import *
    
    ########################
    # クラスCustomerの定義
    ########################
    
    class Customer :
    
    	#####################
    	# コンストラクタ
    	#      s : 状態
    	#      t : 到着時刻
    	#####################
    	def __init__(self, s, t) :
    		self.state = s   # 客の状態
    	                     #   =-1 : 待ち行列に入っている
    	                     #   >=0 : サービスを受けている窓口番号
    		self.time  = t   # 到着時刻
    
    ########################
    # クラスQ_baseの定義
    ########################
    
    class Q_base :
    
    	#########################################
    	# コンストラクタ
    	#      s_i : 窓口の数
    	#      m_a_i : 到着時間間隔の平均値
    	#      m_s_i : サービス時間の平均値
    	#      end_i : シミュレーション終了時刻
    	#########################################
    
    	def __init__(self, s_i, m_a_i, m_s_i, end_i) :
    				# 設定
    		self.s   = s_i   # 窓口の数
    		self.m_a = m_a_i   # 到着時間間隔の平均値
    		self.m_s = m_s_i   # サービス時間の平均値
    		self.end = end_i   # シミュレーション終了時刻
    				# 領域の確保
    		self.sb   = np.zeros(self.s, np.int)   # 各窓口の空き状況
    		                                       #    =0 : 窓口は空いている
    		                                       #    >0 : サービスを受けている客番号
    		self.c_sb = np.zeros(self.s, np.float)   # 各窓口がふさがっている時間の累計
    		self.st_e = np.empty(self.s, np.float)   # 各窓口のサービス終了時刻
    		self.st_s = np.empty(self.s, np.float)   # 各窓口がふさがった時刻
    				# 初期設定
    		self.p_time = 0.0  # 現在時刻
    		self.nc     = 0   # 到着客数カウンタ
    		self.asb    = self.s   # 全窓口の空き状況
    		                       #    =0 : すべての窓口がふさがっている
    		                       #    =n : n個の窓口が空いている
    		self.m_lq   = 0   # 最大待ち行列長
    		self.m_sc   = 0   # 最大系内客数
    		self.c_asb  = 0.0   # すべての窓口がふさがっている時間の累計
    		self.c_wt   = 0.0   # 待ち時間の累計
    		self.m_wt   = 0.0   # 最大待ち時間
    		self.c_lq_t = 0.0   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    		self.lq_t   = 0.0   # 現在の待ち行列長になった時刻
    		self.m_sys  = 0.0   # 最大滞在時間
    		self.c_sys  = 0.0   # 滞在時間の累計
    		self.c_sc_t = 0.0   # 系内客数にその数が継続した時間を乗じた値の累計
    		self.sc_t   = 0.0   # 現在の系内客数になった時刻
    		self.asb_t  = 0.0   # すべての窓口がふさがった時刻
    		self.cus    = dict()   # 系内にいる客のリスト
    		self.que    = []   # 待ち行列
    			# 最初の客の到着時刻の設定
    		self.at = self.p_time + self.Next_at()   # 次の客の到着時刻(負の時は,終了)
    	
    	################################
    	# 次の客の到着までの時間の発生
    	################################
    
    	def Next_at(self) :
    #		return -self.m_a * log(random())
    		return expovariate(1.0 / self.m_a)
    	
    	#####################
    	# サービス時間の発生
    	#####################
    
    	def Next_sv(self) :
    #		return -self.m_s * log(random())-4.0 * log(random())
    		return expovariate(1.0 / self.m_s)
    	
    	##############
    	# 全体の制御
    	##############
    
    	def Control(self) :
    	
    		sw = 0
    		while sw > -2 :
    			sw = self.Next()   # 次の処理の選択
    			if sw == -1 :
    				sw = self.End_o_s()   # シミュレーションの終了
    			else :
    				if sw == 0 :
    					self.Arrive()   # 客の到着処理
    				else :
    					self.Service(sw)   # サービスの終了
    	
    	##################################################
    	# 次の処理の決定
    	#      return : =-1 : シミュレーションの終了
    	#               =0  : 客の到着処理
    	#               =i  : i番目の窓口のサービス終了
    	##################################################
    
    	def Next(self) :
    	
    		sw = -1
    		t  = self.end   # シミュレーション終了時刻で初期設定
    						# 次の客の到着時刻
    		if self.at >= 0.0 and self.at < t :
    			sw = 0
    			t  = self.at
    						# サービス終了時刻
    		for i1 in range(0, self.s) :
    			if self.sb[i1] > 0 and self.st_e[i1] <= t :
    				sw = i1 + 1
    				t  = self.st_e[i1]   # 窓口i1のサービス終了時刻
    	
    		return sw
    	
    	##################################
    	# 終了処理
    	#      return : =-1 : 終了前処理
    	#               =-2 : 実際の終了
    	##################################
    
    	def End_o_s(self) :
    	
    		sw          = -2
    		self.p_time = self.end   # 現在時刻
    		self.at     = -1.0   # 次の客の到着時刻
    	
    		for i1 in range(0, self.s) :
    			if self.sb[i1] > 0 :   # サービス中の客はいるか?
    				if sw == -2 :
    					sw       = -1
    					self.end = self.st_e[i1]   # 窓口i1のサービス終了時刻
    				else :
    					if self.st_e[i1] > self.end :
    						self.end = self.st_e[i1]   # 窓口i1のサービス終了時刻
    	
    		return sw
    	
    	################
    	# 客の到着処理
    	################
    
    	def Arrive(self) :
    				# 客数の増加と次の客の到着時刻の設定
    		self.nc     += 1   # 到着客数カウンタ
    		self.p_time  = self.at   # 現在時刻
    		self.at      = self.p_time + self.Next_at()   # 次の客の到着時刻
    		if self.at >= self.end :
    			self.at = -1.0
    				# 系内客数の処理
    		self.c_sc_t += len(self.cus) * (self.p_time - self.sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
    		self.sc_t    = self.p_time   # 現在の系内客数になった時刻
    		if len(self.cus)+1 > self.m_sc :
    			self.m_sc = len(self.cus) + 1   # 最大系内客数
    				# 窓口に空きがない場合
    		if self.asb == 0 :
    			ct_p               = Customer(-1, self.p_time)
    			self.cus[self.nc]  = ct_p   # 客の登録(系内客数)
    			self.c_lq_t       += len(self.que) * (self.p_time - self.lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    			self.que.append(self.nc)   # 客の登録(待ち行列)
    			self.lq_t = self.p_time   # 現在の待ち行列長になった時刻
    			if len(self.que) > self.m_lq :
    				self.m_lq = len(self.que)   # 最大待ち行列長
    				# すぐサービスを受けられる場合
    		else :
    			k = -1
    			for i1 in range(0, self.s) :
    				if self.sb[i1] == 0 :
    					ct_p               = Customer(i1, self.p_time)
    					self.cus[self.nc]  = ct_p   # 客の登録(系内客数)
    					k                  = i1
    					self.sb[k]         = self.nc   # サービスを受けている客番号
    					self.st_e[k]       = self.p_time + self.Next_sv()   # 窓口kのサービス終了時刻
    					self.asb          -= 1   # 空いている窓口数
    					break
    			self.st_s[k] = self.p_time   # 窓口kがふさがった時刻
    			if self.asb == 0 :
    				self.asb_t = self.p_time   # すべての窓口がふさがった時刻
    	
    	#################################
    	# サービス終了時の処理
    	#      k : サービス終了窓口番号
    	#################################
    
    	def Service(self, k) :
    				# 時間の設定
    		k            -= 1
    		self.p_time   = self.st_e[k]   # 現在時刻
    		self.st_e[k]  = -1.0   # サービス終了時間
    				# 系内客数の処理
    		self.c_sc_t  += len(self.cus) * (self.p_time - self.sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
    		self.sc_t     = self.p_time   # 現在の系内客数になった時刻
    				# 滞在時間の処理
    		x1          = self.p_time - self.cus[self.sb[k]].time
    		self.c_sys += x1   # 滞在時間の累計
    		if x1 > self.m_sys :
    			self.m_sys = x1   # 最大滞在時間
    		self.cus.pop(self.sb[k])   # 客の削除(系内客数)
    				# 窓口使用時間の処理
    		self.asb     += 1   # 空いている窓口数
    		self.sb[k]    = 0   # 窓口kを空き状態にする
    		self.c_sb[k] += (self.p_time - self.st_s[k])   # 窓口kがふさがっている時間の累計
    				# 待ち行列がある場合
    		if len(self.que) > 0 :
    			self.asb    -= 1   # 開いている窓口数
    			self.c_lq_t += len(self.que) * (self.p_time - self.lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
    			n          = self.que.pop(0)
    			self.lq_t  = self.p_time   # 現在の待ち行列長になった時刻
    			x1         = self.p_time - self.cus[n].time
    			self.c_wt += x1   # 待ち時間の累計
    			if x1 > self.m_wt :
    				self.m_wt = x1   # 最大待ち時間
    			k = -1
    			for i1 in range(0, self.s) :
    				if self.sb[i1] == 0 :
    					k                 = i1
    					self.sb[k]        = n   # 窓口kの客番号
    					self.st_e[k]      = self.p_time + self.Next_sv()   # 窓口kのサービス終了時刻
    					self.st_s[k]      = self.p_time   # 窓口kがふさがった時刻
    					self.cus[n].state = k   # 客の状態変更
    					break
    				# 待ち行列がない場合
    		else :
    			if self.asb == 1 :
    				self.c_asb += (self.p_time - self.asb_t)   # すべての窓口がふさがっている時間の累計
    	
    	########################
    	# 結果の出力
    	# (カッコ内は理論値)
    	########################
    
    	def Output(self) :
    	
    		rn  = float(self.nc)
    		rs  = float(self.s)
    		ram = 1.0 / self.m_a
    		myu = 1.0 / self.m_s
    		rou = ram / (rs * myu)
    		if self.s == 1 :
    			p0 = 1.0 - rou
    			pi = rou
    		else :
    			p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)))
    			pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou))
    		Lq = pi * rou / (1.0 - rou)
    		L  = Lq + rs * rou
    		Wq = Lq / ram
    		W  = Wq + 1.0 / myu
    		print("シミュレーション終了時間={0:.3f} 客数={1:d} ρ={2:.3f} p0={3:.3f}".format(self.p_time, self.nc, rou, p0))
    		print("   すべての窓口が塞がっている割合={0:.3f} ({1:.3f})".format(self.c_asb/self.p_time, pi))
    		print("   各窓口が塞がっている割合")
    		for i1 in range(0, self.s) :
    			print("      {0:d}   {1:.3f}".format(i1+1, self.c_sb[i1]/self.p_time))
    		print("   平均待ち行列長={0:.3f} ({1:.3f})  最大待ち行列長={2:d}".format(self.c_lq_t/self.p_time, Lq, self.m_lq))
    		print("   平均系内客数  ={0:.3f} ({1:.3f})  最大系内客数  ={2:d}".format(self.c_sc_t/self.p_time, L, self.m_sc))
    		print("   平均待ち時間  ={0:.3f} ({1:.3f})  最大待ち時間  ={2:.3f}".format(self.c_wt/rn, Wq, self.m_wt))
    		print("   平均滞在時間  ={0:.3f} ({1:.3f})  最大滞在時間  ={2:.3f}".format(self.c_sys/rn, W, self.m_sys))
    
    #############################
    # 簡単な待ち行列問題(M/M/s)
    #      coded by Y.Suganuma
    #############################
    
    			# 入力データ
    s   = int(input("窓口の数は? "))
    end = float(input("シミュレーション終了時間? "))
    m_a = float(input("   到着時間間隔の平均値は? "))
    m_s = float(input("   サービス時間の平均値は? "))
    			# システムの定義
    base = Q_base(s, m_a, m_s, end)
    			# シミュレーションの実行
    seed()
    base.Control()
    			# 出力
    base.Output()
    			

  7. C#

    /******************************/
    /* 簡単な待ち行列問題(M/M/s)*/
    /*      coded by Y.Suganuma   */
    /******************************/
    using System;
    using System.Collections.Generic;
    
    class Program
    {
    	/****************/
    	/* main program */
    	/****************/
    	static void Main()
    	{
    	/*
    	          入力データ
    	*/
    		Console.Write("窓口の数は? ");
    		int s = int.Parse(Console.ReadLine());
    
    		Console.Write("シミュレーション終了時間? ");
    		double end = double.Parse(Console.ReadLine());
    
    		Console.Write("   到着時間間隔の平均値は? ");
    		double m_a = double.Parse(Console.ReadLine());
    
    		Console.Write("   サービス時間の平均値は? ");
    		double m_s = double.Parse(Console.ReadLine());
    	/*
    	          システムの定義
    	*/
    		Q_base base1 = new Q_base(s, m_a, m_s, end);
    	/*
    	          シミュレーションの実行
    	*/
    		base1.Control();
    	/*
    	          出力
    	*/
    		base1.Output();
    	}
    }
    
    /**********************/
    /* クラスQ_baseの定義 */
    /**********************/
    class Q_base {
    	int s;   // 窓口の数
    	int asb;   // 全窓口の空き状況
                   //    =0 : すべての窓口がふさがっている
                   //    =n : n個の窓口が空いている
    	int [] sb;   // 各窓口の空き状況
                     //    =0 : 窓口は空いている
                     //    >0 : サービスを受けている客番号
    	double asb_t;   // すべての窓口がふさがった時間
    	double c_asb;   // すべての窓口がふさがっている時間の累計
    	double [] c_sb;   // 各窓口がふさがっている時間の累計
    	double [] st_e;   // 各窓口のサービス終了時間
    	double [] st_s;   // 各窓口がふさがった時間
    	int m_lq;   // 最大待ち行列長
    	double c_lq_t;   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    	double c_wt;   // 待ち時間の累計
    	double lq_t;   // 現在の待ち行列長になった時間
    	double m_wt;   // 最大待ち時間
    	double c_sc_t;   // 系内客数にその数が継続した時間を乗じた値の累計
    	double c_sys;   // 滞在時間の累計
    	double m_sys;   // 最大滞在時間
    	double sc_t;   // 現在の系内客数になった時間
    	int m_sc;   // 最大系内客数
    	double m_a;   // 到着時間間隔の平均値
    	double m_s;   // サービス時間の平均値
    	double at;   // 次の客の到着時間(負の時は,終了)
    	double p_time;   // 現在時間
    	double end;   // シミュレーション終了時間
    	int nc;   // 到着客数カウンタ
    	Random rn;   // 乱数
    	Dictionary<int, Customer> cus;   // 系内にいる客のリスト
    	Queue<int> que;   // 待ち行列
    
    	/*****************************************/
    	/* コンストラクタ                        */
    	/*      s_i : 窓口の数                   */
    	/*      m_a_i : 到着時間間隔の平均値     */
    	/*      m_s_i : サービス時間の平均値     */
    	/*      end_i : シミュレーション終了時間 */
    	/*****************************************/
    	public Q_base (int s_i, double m_a_i, double m_s_i, double end_i)
    	{
    	/*
    	          設定
    	*/
    		s   = s_i;
    		m_a = m_a_i;
    		m_s = m_s_i;
    		end = end_i;
    	/*
    	          領域の確保
    	*/
    		sb   = new int [s];
    		c_sb = new double [s];
    		st_e = new double [s];
    		st_s = new double [s];
    
    		for (int i1 = 0; i1 < s; i1++) {
    			sb[i1]   = 0;
    			c_sb[i1] = 0.0;
    		}
    
    		cus = new Dictionary <int, Customer> ();
    		que = new Queue <int> ();
    	/*
    	          初期設定
    	*/
    		p_time = 0.0;
    		nc     = 0;
    		asb    = s;
    		m_lq   = 0;
    		m_sc   = 0;
    		c_asb  = 0.0;
    		c_wt   = 0.0;
    		m_wt   = 0.0;
    		c_lq_t = 0.0;
    		lq_t   = 0.0;
    		m_sys  = 0.0;
    		c_sys  = 0.0;
    		c_sc_t = 0.0;
    		sc_t   = 0.0;
    	/*
    	          乱数の初期設定
    	*/
    		rn = new Random();
    	/*
    	          最初の客の到着時間の設定
    	*/
    		at = p_time + Next_at();
    	}
    
    	/**********************************/
    	/* 次の客の到着までの時間の発生   */
    	/**********************************/
    	double Next_at()
    	{
    		return -m_a * Math.Log(rn.NextDouble());
    	}
    
    	/************************/
    	/* サービス時間の発生   */
    	/************************/
    	double Next_sv()
    	{
    		return -m_s * Math.Log(rn.NextDouble());
    	}
    
    	/**************/
    	/* 全体の制御 */
    	/**************/
    	public void Control()
    	{
    		int sw = 0;
    		while (sw > -2) {
    			sw = Next();   // 次の処理の選択
    			if (sw == -1)
    				sw = End_o_s();   // シミュレーションの終了
    			else {
    				if (sw == 0)
    					Arrive();   // 客の到着処理
    				else
    					Service(sw);   // サービスの終了
    			}
    		}
    	}
    
    	/**************************************************/
    	/* 次の処理の決定                                 */
    	/*      return : =-1 : シミュレーションの終了     */
    	/*               =0  : 客の到着処理               */
    	/*               =i  : i番目の窓口のサービス終了 */
    	/**************************************************/
    	int Next()
    	{
    		int sw   = -1;
    		double t = end;   // シミュレーション終了時刻で初期設定
    					// 次の客の到着時刻
    		if (at >= 0.0 && at < t) {
    			sw = 0;
    			t  = at;
    		}
    					// サービス終了時刻
    		for (int i1 = 0; i1 < s; i1++) {
    			if (sb[i1] > 0 && st_e[i1] <= t) {
    				sw = i1 + 1;
    				t  = st_e[i1];   // 窓口i1のサービス終了時刻
    			}
    		}
    
    		return sw;
    	}
    
    	/**********************************/
    	/* 終了処理                       */
    	/*      return : =-1 : 終了前処理 */
    	/*               =-2 : 実際の終了 */
    	/**********************************/
    	int End_o_s()
    	{
    		int sw = -2;
    		p_time = end;   // 現在時刻
    		at     = -1.0;   // 次の客の到着時刻
    
    		for (int i1 = 0; i1 < s; i1++) {
    			if (sb[i1] > 0) {   // サービス中の客はいるか?
    				if (sw == -2) {
    					sw  = -1;
    					end = st_e[i1];   // 窓口i1のサービス終了時刻
    				}
    				else {
    					if (st_e[i1] > end)
    						end = st_e[i1];   // 窓口i1のサービス終了時刻
    				}
    			}
    		}
    
    		return sw;
    	}
    
    	/****************************/
    	/* 客の到着処理             */
    	/*      ct : 客リストの先頭 */
    	/****************************/
    	void Arrive()
    	{
    	/*
    	          客数の増加と次の客の到着時刻の設定
    	*/
    		nc     += 1;   // 到着客数カウンタ
    		p_time  = at;   // 現在時刻
    		at      = p_time + Next_at();      // 次の客の到着時刻
    		if (at >= end)
    			at = -1.0;
    	/*
    	          系内客数の処理
    	*/
    		c_sc_t += cus.Count * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    		sc_t    = p_time;   // 現在の系内客数になった時刻
    		if (cus.Count+1 > m_sc)
    			m_sc = cus.Count + 1;   // 最大系内客数
    	/*
    	          窓口に空きがない場合
    	*/
    		if (asb == 0) {
    			Customer ct_p = new Customer(-1, p_time);
    			cus.Add(nc, ct_p);   // 客の登録(系内客数)
    			c_lq_t += que.Count * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			que.Enqueue(nc);   // 客の登録(待ち行列)
    			lq_t = p_time;   // 現在の待ち行列長になった時刻
    			if (que.Count > m_lq)
    				m_lq = que.Count;   // 最大待ち行列長
    		}
    	/*
    	          すぐサービスを受けられる場合
    	*/
    		else {
    			int k = -1;
    			for (int i1 = 0; i1 < s && k < 0; i1++) {
    				if (sb[i1] == 0) {
    					Customer ct_p = new Customer(i1, p_time);
    					cus.Add(nc, ct_p);   // 客の登録(系内客数)
    					k        = i1;
    					sb[k]    = nc;   // サービスを受けている客番号
    					st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    					asb     -= 1;   // 空いている窓口数
    				}
    			}
    			st_s[k] = p_time;   // 窓口kがふさがった時刻
    			if (asb == 0)
    				asb_t = p_time;   // すべての窓口がふさがった時刻
    		}
    	}
    
    	/*********************************/
    	/* サービス終了時の処理          */
    	/*      k : サービス終了窓口番号 */
    	/*      ct : 客リストの先頭      */
    	/*********************************/
    	void Service(int k)
    	{
    	/*
    	          時間の設定
    	*/
    		k      -= 1;
    		p_time  = st_e[k];   // 現在時刻
    		st_e[k] = -1.0;   // サービス終了時間
    	/*
    	          系内客数の処理
    	*/
    		c_sc_t += cus.Count * (p_time - sc_t);   // 系内客数にその数が継続した時間を乗じた値の累計
    		sc_t    = p_time;   // 現在の系内客数になった時刻
    	/*
    	          滞在時間の処理
    	*/
    		Customer ct = cus[sb[k]];
    		double x1   = p_time - ct.time;
    		c_sys += x1;   // 滞在時間の累計
    		if (x1 > m_sys)
    			m_sys = x1;   // 最大滞在時間
    		cus.Remove(sb[k]);   // 客の削除(系内客数)
    	/*
    	          窓口使用時間の処理
    	*/
    		asb     += 1;   // 空いている窓口数
    		sb[k]    = 0;   // 窓口kを空き状態にする
    		c_sb[k] += (p_time - st_s[k]);   // 窓口kがふさがっている時間の累計
    	/*
    	          待ち行列がある場合
    	*/
    		if (que.Count > 0) {
    			asb    -= 1;   // 開いている窓口数
    			c_lq_t += que.Count * (p_time - lq_t);   // 待ち行列長にその長さが継続した時間を乗じた値の累計
    			int n  = que.Dequeue();   // 客の削除(待ち行列)
    			lq_t   = p_time;   // 現在の待ち行列長になった時刻
    			ct     = cus[n];
    			x1     = p_time - ct.time;
    			c_wt  += x1;   // 待ち時間の累計
    			if (x1 > m_wt)
    				m_wt = x1;   // 最大待ち時間
    			k = -1;
    			for (int i1 = 0; i1 < s && k < 0; i1++) {
    				if (sb[i1] == 0) {
    					k        = i1;
    					sb[k]    = n;   // 窓口kの客番号
    					st_e[k]  = p_time + Next_sv();   // 窓口kのサービス終了時刻
    					st_s[k]  = p_time;   // 窓口kがふさがった時刻
    					ct.state = k;   // 客の状態変更
    				}
    			}
    		}
    	/*
    	          待ち行列がない場合
    	*/
    		else {
    			if (asb == 1)
    				c_asb += (p_time - asb_t);   // すべての窓口がふさがっている時間の累計
    		}
    	}
    
    	/************************/
    	/* 結果の出力           */
    	/* (カッコ内は理論値) */
    	/************************/
    	public void Output()
    	{
    		double rn  = (double)nc;
    		double rs  = (double)s;
    		double ram = 1.0 / m_a;
    		double myu = 1.0 / m_s;
    		double rou = ram / (rs * myu);
    		double p0, pi;
    		if (s == 1) {
    			p0 = 1.0 - rou;
    			pi = rou;
    		}
    		else {
    			p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)));
    			pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou));
    		}
    		double Lq = pi * rou / (1.0 - rou);
    		double L  = Lq + rs * rou;
    		double Wq = Lq / ram;
    		double W  = Wq + 1.0 / myu;
    		Console.WriteLine("シミュレーション終了時間=" + p_time + " 客数=" + nc + " ρ=" + rou + " p0=" + p0);
    		Console.WriteLine("   すべての窓口が塞がっている割合=" + (c_asb/p_time) + " (" + pi + ")");
    		Console.WriteLine("   各窓口が塞がっている割合");
    		for (int i1 = 0; i1 < s; i1++)
    			Console.WriteLine("      " + (i1+1) + "   " + (c_sb[i1]/p_time));
    		Console.WriteLine("   平均待ち行列長=" + (c_lq_t/p_time) + " (" + Lq + ")  最大待ち行列長=" + m_lq);
    		Console.WriteLine("   平均系内客数  =" + (c_sc_t/p_time) + " (" + L + ")  最大系内客数  =" + m_sc);
    		Console.WriteLine("   平均待ち時間  =" + (c_wt/rn) + " (" + Wq + ")  最大待ち時間  =" + m_wt);
    		Console.WriteLine("   平均滞在時間  =" + (c_sys/rn) + " (" + W + ")  最大滞在時間  =" + m_sys);
    	}
    }
    
    /************************/
    /* クラスCustomerの定義 */
    /************************/
    class Customer
    {
    	public double time;   // 到着時刻
    	public int state;   // 客の状態
                             //   =-1 : 待ち行列に入っている
                             //   >=0 : サービスを受けている窓口番号
    	/*******************/
    	/* コンストラクタ  */
    	/*      n : 客番号 */
    	/*      s : 状態   */
    	/*******************/
    	public Customer (int s, double t)
    	{
    		time  = t;
    		state = s;
    	}
    }
    			

  8. VB

    '****************************'
    ' 簡単な待ち行列問題(M/M/s)'
    '      coded by Y.Suganuma   '
    '****************************'
    Imports System.Collections.Generic
    
    Module Test
    
    	Sub Main()
    				'
    				' 入力データ
    				'
    		Console.Write("窓口の数は? ")
    		Dim s As Integer = Integer.Parse(Console.ReadLine())
    
    		Console.Write("シミュレーション終了時間? ")
    		Dim end_s As Double = Double.Parse(Console.ReadLine())
    
    		Console.Write("   到着時間間隔の平均値は? ")
    		Dim m_a As Double = Double.Parse(Console.ReadLine())
    
    		Console.Write("   サービス時間の平均値は? ")
    		Dim m_s As Double = Double.Parse(Console.ReadLine())
    				'
    				' システムの定義
    				'
    		Dim base1 As Q_base = new Q_base(s, m_a, m_s, end_s)
    				'
    				' シミュレーションの実行
    				'
    		base1.Control()
    				'
    				' 出力
    				'
    		base1.Output()
    
    	End Sub
    
    	'********************'
    	' クラスQ_baseの定義 '
    	'********************'
    	Class Q_base
    
    		Private s As Integer   ' 窓口の数
    		Private asb As Integer   ' 全窓口の空き状況
    		                         '    =0 : すべての窓口がふさがっている
    		                         '    =n : n個の窓口が空いている
    		Private sb() As Integer   ' 各窓口の空き状況
    		                          '    =0 : 窓口は空いている
    		                          '    >0 : サービスを受けている客番号
    		Private asb_t As Double   ' すべての窓口がふさがった時間
    		Private c_asb As Double   ' すべての窓口がふさがっている時間の累計
    		Private c_sb() As Double   ' 各窓口がふさがっている時間の累計
    		Private st_e() As Double   ' 各窓口のサービス終了時間
    		Private st_s() As Double   ' 各窓口がふさがった時間
    		Private m_lq As Integer   ' 最大待ち行列長
    		Private c_lq_t As Double   ' 待ち行列長にその長さが継続した時間を乗じた値の累計
    		Private c_wt As Double   ' 待ち時間の累計
    		Private lq_t As Double   ' 現在の待ち行列長になった時間
    		Private m_wt As Double   ' 最大待ち時間
    		Private c_sc_t As Double   ' 系内客数にその数が継続した時間を乗じた値の累計
    		Private c_sys As Double   ' 滞在時間の累計
    		Private m_sys As Double   ' 最大滞在時間
    		Private sc_t As Double   ' 現在の系内客数になった時間
    		Private m_sc As Integer   ' 最大系内客数
    		Private m_a As Double   ' 到着時間間隔の平均値
    		Private m_s As Double   ' サービス時間の平均値
    		Private at As Double   ' 次の客の到着時間(負の時は,終了)
    		Private p_time As Double   ' 現在時間
    		Private end_s As Double   ' シミュレーション終了時間
    		Private nc As Integer   ' 到着客数カウンタ
    		Private rn As Random   ' 乱数
    		Private cus As New Dictionary(Of Integer, Customer)   ' 系内にいる客のリスト
    		Private que As New Queue(Of Integer)   ' 待ち行列
    	
    		'***************************************'
    		' コンストラクタ                        '
    		'      s_i : 窓口の数                   '
    		'      m_a_i : 到着時間間隔の平均値     '
    		'      m_s_i : サービス時間の平均値     '
    		'      end_i : シミュレーション終了時間 '
    		'***************************************'
    		Public Sub New(s_i As Integer, m_a_i As Double, m_s_i As Double, end_i As Double)
    				'
    				' 設定
    				'
    			s     = s_i
    			m_a   = m_a_i
    			m_s   = m_s_i
    			end_s = end_i
    				'
    				' 領域の確保
    				'
    			ReDim sb(s)
    			ReDim c_sb(s)
    			ReDim st_e(s)
    			ReDim st_s(s)
    
    			For i1 As Integer = 0 To s-1
    				sb(i1)   = 0
    				c_sb(i1) = 0.0
    			Next
    				'
    				' 初期設定
    				'
    			p_time = 0.0
    			nc     = 0
    			asb    = s
    			m_lq   = 0
    			m_sc   = 0
    			c_asb  = 0.0
    			c_wt   = 0.0
    			m_wt   = 0.0
    			c_lq_t = 0.0
    			lq_t   = 0.0
    			m_sys  = 0.0
    			c_sys  = 0.0
    			c_sc_t = 0.0
    			sc_t   = 0.0
    				'
    				' 乱数の初期設定
    				'
    			rn = new Random()
    				'
    				' 最初の客の到着時間の設定
    				'
    			at = p_time + Next_at()
    
    		End Sub
    
    		'********************************'
    		' 次の客の到着までの時間の発生   '
    		'********************************'
    		Function Next_at()
    			Return -m_a * Math.Log(rn.NextDouble())
    		End Function
    
    		'**********************'
    		' サービス時間の発生   '
    		'**********************'
    		Function Next_sv()
    			Return -m_s * Math.Log(rn.NextDouble())
    		End Function
    
    		'************'
    		' 全体の制御 '
    		'************'
    		Sub Control()
    
    			Dim sw As Integer = 0
    			Do While sw > -2
    				sw = Next_e()   ' 次の処理の選択
    				If sw = -1
    					sw = End_o_s()   ' シミュレーションの終了
    				Else
    					If sw = 0
    						Arrive()   ' 客の到着処理
    					Else
    						Service(sw)   ' サービスの終了
    					End If
    				End If
    			Loop
    
    		End Sub
    
    		'************************************************'
    		' 次の処理の決定                                 '
    		'      return : =-1 : シミュレーションの終了     '
    		'               =0  : 客の到着処理               '
    		'               =i  : i番目の窓口のサービス終了 '
    		'************************************************'
    		Function Next_e()
    
    			Dim sw As Integer = -1
    			Dim t As Double   = end_s   ' シミュレーション終了時刻で初期設定
    					' 次の客の到着時刻
    			If at >= 0.0 and at < t
    				sw = 0
    				t  = at
    			End If
    					' サービス終了時刻
    			For i1 As Integer = 0 To s-1
    				If sb(i1) > 0 and st_e(i1) <= t
    					sw = i1 + 1
    					t  = st_e(i1)   ' 窓口i1のサービス終了時刻
    				End If
    			Next
    
    			Return sw
    
    		End Function
    
    		'********************************'
    		' 終了処理                       '
    		'      return : =-1 : 終了前処理 '
    		'               =-2 : 実際の終了 '
    		'********************************'
    		Function End_o_s()
    
    			Dim sw As Integer = -2
    			p_time            = end_s   ' 現在時刻
    			at                = -1.0   ' 次の客の到着時刻
    
    			For i1 As Integer = 0 To s-1
    				If sb(i1) > 0   ' サービス中の客はいるか?
    					If sw = -2
    						sw  = -1
    						end_s = st_e(i1)   ' 窓口i1のサービス終了時刻
    					Else
    						If st_e(i1) > end_s
    							end_s = st_e(i1)   ' 窓口i1のサービス終了時刻
    						End If
    					End If
    				End If
    			Next
    
    			Return sw
    
    		End Function
    
    		'**************************'
    		' 客の到着処理             '
    		'      ct : 客リストの先頭 '
    		'**************************'
    		Sub Arrive()
    				'
    				' 客数の増加と次の客の到着時刻の設定
    				'
    			nc     += 1   ' 到着客数カウンタ
    			p_time  = at   ' 現在時刻
    			at      = p_time + Next_at()      ' 次の客の到着時刻
    			If at >= end_s
    				at = -1.0
    			End If
    				'
    				' 系内客数の処理
    				'
    			c_sc_t += cus.Count * (p_time - sc_t)   ' 系内客数にその数が継続した時間を乗じた値の累計
    			sc_t    = p_time   ' 現在の系内客数になった時刻
    			If cus.Count+1 > m_sc
    				m_sc = cus.Count + 1   ' 最大系内客数
    			End If
    				'
    				' 窓口に空きがない場合
    				'
    			If asb = 0
    				Dim ct_p As Customer = new Customer(-1, p_time)
    				cus.Add(nc, ct_p)   ' 客の登録(系内客数)
    				c_lq_t += que.Count * (p_time - lq_t)   ' 待ち行列長にその長さが継続した時間を乗じた値の累計
    				que.Enqueue(nc)   ' 客の登録(待ち行列)
    				lq_t = p_time   ' 現在の待ち行列長になった時刻
    				If que.Count > m_lq
    					m_lq = que.Count   ' 最大待ち行列長
    				End If
    				'
    				' すぐサービスを受けられる場合
    				'
    			Else
    				Dim k As Integer   = -1
    				Dim i1t As Integer = 0
    				Do While i1t < s and k < 0
    					If sb(i1t) = 0
    						Dim ct_p As Customer = new Customer(i1t, p_time)
    						cus.Add(nc, ct_p)   ' 客の登録(系内客数)
    						k        = i1t
    						sb(k)    = nc   ' サービスを受けている客番号
    						st_e(k)  = p_time + Next_sv()   ' 窓口kのサービス終了時刻
    						asb     -= 1   ' 空いている窓口数
    					End If
    					i1t += 1
    				Loop
    				st_s(k) = p_time   ' 窓口kがふさがった時刻
    				If asb = 0
    					asb_t = p_time   ' すべての窓口がふさがった時刻
    				End If
    			End If
    
    		End Sub
    
    		'*******************************'
    		' サービス終了時の処理          '
    		'      k : サービス終了窓口番号 '
    		'      ct : 客リストの先頭      '
    		'*******************************'
    		Sub Service(k As Integer)
    				'
    				' 時間の設定
    				'
    			k      -= 1
    			p_time  = st_e(k)   ' 現在時刻
    			st_e(k) = -1.0   ' サービス終了時間
    				'
    				' 系内客数の処理
    				'
    			c_sc_t += cus.Count * (p_time - sc_t)   ' 系内客数にその数が継続した時間を乗じた値の累計
    			sc_t    = p_time   ' 現在の系内客数になった時刻
    				'
    				' 滞在時間の処理
    				'
    			Dim ct As Customer = cus(sb(k))
    			Dim x1 As Double   = p_time - ct.time
    			c_sys += x1   ' 滞在時間の累計
    			If x1 > m_sys
    				m_sys = x1   ' 最大滞在時間
    			End If
    			cus.Remove(sb(k))   ' 客の削除(系内客数)
    				'
    				' 窓口使用時間の処理
    				'
    			asb     += 1   ' 空いている窓口数
    			sb(k)    = 0   ' 窓口kを空き状態にする
    			c_sb(k) += (p_time - st_s(k))   ' 窓口kがふさがっている時間の累計
    				'
    				' 待ち行列がある場合
    				'
    			If que.Count > 0
    				asb    -= 1   ' 開いている窓口数
    				c_lq_t += que.Count * (p_time - lq_t)   ' 待ち行列長にその長さが継続した時間を乗じた値の累計
    				Dim n As Integer = que.Dequeue()   ' 客の削除(待ち行列)
    				lq_t   = p_time   ' 現在の待ち行列長になった時刻
    				ct     = cus(n)
    				x1     = p_time - ct.time
    				c_wt  += x1   ' 待ち時間の累計
    				If x1 > m_wt
    					m_wt = x1   ' 最大待ち時間
    				End If
    				k = -1
    				Dim i1t As Integer = 0
    				Do While i1t < s and k < 0
    					If sb(i1t) = 0
    						k        = i1t
    						sb(k)    = n   ' 窓口kの客番号
    						st_e(k)  = p_time + Next_sv()   ' 窓口kのサービス終了時刻
    						st_s(k)  = p_time   ' 窓口kがふさがった時刻
    						ct.state = k   ' 客の状態変更
    					End If
    					i1t += 1
    				Loop
    				'
    				' 待ち行列がない場合
    				'
    			Else
    				If asb = 1
    					c_asb += (p_time - asb_t)   ' すべての窓口がふさがっている時間の累計
    				End If
    			End If
    
    		End Sub
    
    		'**********************'
    		' 結果の出力           '
    		' (カッコ内は理論値) '
    		'**********************'
    		Sub Output()
    
    			Dim rn As Double  = nc
    			Dim rs As Double  = s
    			Dim ram As Double = 1.0 / m_a
    			Dim myu As Double = 1.0 / m_s
    			Dim rou As Double = ram / (rs * myu)
    			Dim p0 As Double
    			Dim pi As Double
    			If s = 1
    				p0 = 1.0 - rou
    				pi = rou
    			Else
    				p0 = 1.0 / (1.0 + 2.0 * rou + 4.0 * rou * rou / (2.0 * (1.0 - rou)))
    				pi = 4.0 * rou * rou * p0 / (2.0 * (1.0 - rou))
    			End If
    			Dim Lq As Double = pi * rou / (1.0 - rou)
    			Dim L As Double  = Lq + rs * rou
    			Dim Wq As Double = Lq / ram
    			Dim W As Double  = Wq + 1.0 / myu
    			Console.WriteLine("シミュレーション終了時間=" & p_time & " 客数=" & nc & " ρ=" & rou & " p0=" & p0)
    			Console.WriteLine("   すべての窓口が塞がっている割合=" & (c_asb/p_time) & " (" & pi & ")")
    			Console.WriteLine("   各窓口が塞がっている割合")
    			For i1 As Integer = 0 To s-1
    				Console.WriteLine("      " & (i1+1) & "   " & (c_sb(i1)/p_time))
    			Next
    			Console.WriteLine("   平均待ち行列長=" & (c_lq_t/p_time) & " (" & Lq & ")  最大待ち行列長=" & m_lq)
    			Console.WriteLine("   平均系内客数  =" & (c_sc_t/p_time) & " (" & L & ")  最大系内客数  =" & m_sc)
    			Console.WriteLine("   平均待ち時間  =" & (c_wt/rn) & " (" & Wq & ")  最大待ち時間  =" & m_wt)
    			Console.WriteLine("   平均滞在時間  =" & (c_sys/rn) & " (" & W & ")  最大滞在時間  =" & m_sys)
    
    		End Sub
    
    	End Class
    
    	'**********************'
    	' クラスCustomerの定義 '
    	'**********************'
    	Class Customer
    
    		Public time As Double   ' 到着時刻
    		Public state As Integer   ' 客の状態
    	                              '   =-1 : 待ち行列に入っている
    	                              '   >=0 : サービスを受けている窓口番号
    		'*****************'
    		' コンストラクタ  '
    		'      n : 客番号 '
    		'      s : 状態   '
    		'*****************'
    		Public Sub New(s As Integer, t As Double)
    			time  = t
    			state = s
    		End Sub
    
    	End Class
    
    End Module
    			

情報学部 菅沼ホーム 目次 索引