Python 待ち行列のシミュレーション

Pythonプログラミング実習 シミュレーション(待ち行列

 「高等学校 情報Ⅰ(数研出版)」の教科書に,1台のATMの平均待ち時間を求めるシミュレーションの例が記載されています。項目が多く複雑に見える内容をPythonソースコードに反映するときの考え方について記載しますので,参考にしてください。

計算する項目

 まずはプログラムで扱う要素を整理しましょう。
 教科書では待ち行列のモデル化の説明と,表計算ソフトウェアで各項目を計算する式の例が記載されています。項目の方に着目して表にしてみます。

No.項目名計算式
1 到着間隔(分) 最初の客は0とする
1~12までの整数を乱数で発生させる
2 到着時刻(分) 最初の客は0とする
前の客の到着時刻+到着間隔
3 開始時刻(分) 前の客の終了時刻>到着時刻 なら 前の客の終了時刻
そうでなければ到着時刻
4 サービス時間(分) つねに5分
5 終了時刻(分) 開始時刻+サービス時間
6 待ち時間(分) 開始時刻-到着時刻
7 平均待ち時間(分) 待ち時間の合計÷全体の人数
 No.1の到着間隔は最初の客だけ0で,以降の客は乱数を使うように処理を分岐すれば良さそうです。
 No.2の到着時刻は,到着時刻と到着間隔の初期値を0にすれば,最初の客も同じ計算式で0になります。
 No.4のサービス時間は教科書のモデル化の本文にあわせ5分固定としますが,後で3~7分までの乱数に変更する例も示します。

 

プログラム例

 <プログラムの流れ>
 ・各項目の変数を準備する。(初期値を代入)
 ・見出し行の表示
 ・whileループで以下を10回繰り返す
  項目1~6を計算式に従って計算する。
  待ち時間を配列(リスト)に追加する。
  客の各項目を表示。(客の番号は i+1 にする)
 ・待ち時間リストから平均待ち時間を計算して画面に表示する。

10人の客の平均待ち時間
# 待ち行列のシミュレーション 10人
import random

tm1_interval = 0    # 到着間隔
tm2_arrival = 0     # 到着時刻
tm3_start = 0       # 開始時刻
tm4_service = 5     # サービス時間
tm5_end = 0         # 終了時刻
tm6_wait = 0        # 待ち時間
wait = []           # 待ち時間のリスト

# 見出し行表示
print("客 到着時刻 開始時刻 S時間 終了時刻 待ち時間")

# 0から9までの繰り返し
i = 0
while i < 10:
    if 0 < i:
        tm1_interval = random.randint(1,12)     # 1-12の乱数
    
    tm2_arrival = tm2_arrival + tm1_interval
    if tm5_end > tm2_arrival:
        tm3_start = tm5_end
    else:
        tm3_start = tm2_arrival

    tm5_end = tm3_start + tm4_service
    tm6_wait = tm3_start - tm2_arrival
    wait.append(tm6_wait)
    # 画面表示
    print(i+1,
          tm2_arrival,
          tm3_start,
          tm4_service,
          tm5_end,
          tm6_wait )
    i = i + 1

# 平均待ち時間
num = len(wait)     # リストの長さ(要素数)を取得
total = sum(wait)   # リストの要素の合計を取得
avg = total / num
print("平均待ち時間", avg , "分")

実行結果

客 到着時刻 開始時刻 S時間 終了時刻 待ち時間
1 0 0 5 5 0
2 8 8 5 13 0
3 18 18 5 23 0
4 20 23 5 28 3
5 30 30 5 35 0
6 36 36 5 41 0
7 38 41 5 46 3
8 43 46 5 51 3
9 45 51 5 56 6
10 53 56 5 61 3
平均待ち時間 1.8 分

※結果は実行のたびに変わる

・各項目の変数名
 変数名はaやbでも機能しますが,何を扱う変数なのか内容がわかる名前にするとソースコードが読みやすくなります。

・whileループ
 i=0から始まるので,最初の客はif i<0 :の中の処理(1-12の乱数代入)は行われません。tm1_intervalの値は初期値の0で計算されます。

・配列(リスト)
 同じ種類のデータを複数格納できる変数です。
 wait = [] で初期化して,空の配列を用意します。
 リスト名.append(値) で配列にデータを追加できます。
 プログラム例では計算した待ち時間を配列waitに追加していきます。(tm1_interval ~ tm6_wait の変数はループの中で次の客のデータで上書きされますが,配列に格納した待ち時間の値は保持することができます)

 wait[0] ← 最初の客の待ち時間が格納されている
 wait[1] ← 2人目の客の待ち時間が格納されている
 wait[2]
 ・・・
 wait[8]
 wait[9] ← 10人目の客の待ち時間が格納されている
 
 

100人の客の平均待ち時間

 客の人数を100名にします。(whileループの回数を100回に変更)
 項目No.4 サービス時間を3~7までの整数を乱数で表すように変更します。

# 待ち行列のシミュレーション 100人 サービス時間の変更 (★印の2カ所変更)
import random

tm1_interval = 0    # 到着間隔
tm2_arrival = 0     # 到着時刻
tm3_start = 0       # 開始時刻
tm4_service = 5     # サービス時間
tm5_end = 0         # 終了時刻
tm6_wait = 0        # 待ち時間
wait = []           # 待ち時間のリスト

# 見出し行表示
print("客 到着時刻 開始時刻 S時間 終了時刻 待ち時間")

# 0から99までの繰り返し
i = 0
while i < 100:  # 10を100に変更 ★
    if 0 < i:
        tm1_interval = random.randint(1,12)     # 1-12の乱数
    
    tm2_arrival = tm2_arrival + tm1_interval
    if tm5_end > tm2_arrival:
        tm3_start = tm5_end
    else:
        tm3_start = tm2_arrival
    tm4_service = random.randint(3,7)     # 3-7の乱数 ★
    tm5_end = tm3_start + tm4_service
    tm6_wait = tm3_start - tm2_arrival
    wait.append(tm6_wait)
    # 画面表示
    print(i+1,
          tm2_arrival,
          tm3_start,
          tm4_service,
          tm5_end,
          tm6_wait )
    i = i + 1

# 平均待ち時間
num = len(wait)     # リストの長さ(要素数)を取得
total = sum(wait)   # リストの要素の合計を取得
avg = total / num
print("平均待ち時間", avg , "分")

実行結果

客 到着時刻 開始時刻 S時間 終了時刻 待ち時間
1 0 0 6 6 0
2 8 8 3 11 0
3 9 11 7 18 2
4 11 18 5 23 7
5 20 23 4 27 3
6 29 29 7 36 0
・・・
99 630 630 5 635 0
100 634 635 4 639 1
平均待ち時間 2.41 分

 

【参考】数字の画面表示位置を揃えたいとき

 画面に表示する位置を揃えたいときは,Pythonの「書式指定文字列」の機能が利用できます。(詳細は文字列のフォーマットや書式指定で調べてみてください)

「文字列.format(引数)」で文字列の書式化操作を行います。
 "{書式指定}".format(引数) ← 文字列の中に{}で置換フィールドを指定
 "{:n}".format(数値)  ← {}で指定したn桁の文字列に変わる

>>>"{:8}".format(1234)
'    1234'

{}の置換フィールドと引数は複数指定できます。
待ち行列のシミュレーションで画面表示を変更する例は以下になります。

print(" 客 到着時刻 開始時刻 S時間 終了時刻 待ち時間")     # 文字列先頭に半角空白追加
・・・
    # 画面表示
    print("{:3}{:8}{:8}{:6}{:8}{:8}".format(
        i+1,
        tm2_arrival,
        tm3_start,
        tm4_service,
        tm5_end,
        tm6_wait ))

 客 到着時刻 開始時刻 S時間 終了時刻 待ち時間
  1       0       0     4       4       0
  2      10      10     6      16       0
・・・
100     671     674     7     681       3
平均待ち時間 1.62 分

  

【参考】ヒストグラム表示追加

 待ち時間のデータの分布をヒストグラムに表す例です。プログラムの一番最後にグラフ描画処理を追加します。

(省略)
・・・

# 平均待ち時間
num = len(wait)     # リストの長さ(要素数)を取得
total = sum(wait)   # リストの要素の合計を取得
avg = total / num
print("平均待ち時間", avg , "分")

# ヒストグラムの表示 ★
import matplotlib.pyplot as plt
plt.hist(wait)
plt.show()

実行結果
 
 ※実行にはグラフ描画ライブラリのmatplotlibのインストールが必要です。エラーになる場合は
Python グラフ描画のプログラム実行でエラー - 勉強ボックス管理者ブログの記事を参照してください。

 

関連記事

kinutani.hateblo.jp