🎓 レベル:標準 | 重要度:A(必須)
📎 前提:待ち行列の基礎とリトルの法則(指標 L,Lq,W,Wq・稼働率 ρ・L=λW) | 確率の土台:確率過程(マルコフ連鎖・ポアソン過程)(連続時間マルコフ・詳細釣り合い)・指数分布・ガンマ分布・ベータ分布(指数の無記憶性) | 次:M/M/c 待ち行列と窓口設計
要点(BLUF)
- M/M/1 はポアソン到着(率 )・指数サービス(率 )・単一窓口。系内数 を状態とすると、無記憶性のおかげで**隣の状態にしか移らない連続時間マルコフ連鎖(birth-death 過程)**になります。
- 定常状態では、状態 と の境界を上り(到着)と下り(サービス完了)で渡る流量が釣り合うので 。これを解くと定常分布 (公比 の幾何分布)。
- 安定条件 は、(幾何級数)が収束して確率の総和を 1 にできる条件そのものです。 では行列が無限に伸びて定常分布が存在しません。
- から指標が一気に出ます——、、、。待たされる確率(窓口が塞がっている確率)は 、とくに 。すべて が効いて で発散します。
1. M/M/1 を birth-death 過程として立てる
M/M/1 の系内数 (サービス中も含めた人数)を状態とします。M(指数・無記憶)の威力はここで効きます——到着間隔も残りサービス時間も無記憶(指数分布・ガンマ分布・ベータ分布)なので、「いま何人いるか」さえ分かれば、過去の経緯(誰がいつ来たか)を一切知らなくても将来の確率が決まります。つまり は連続時間マルコフ連鎖です。
しかも微小時間 の間に起きうるのは「到着1件()」か「サービス完了1件()」だけで、2件同時はほぼ起きません(ポアソン過程の希少性、確率過程(マルコフ連鎖・ポアソン過程))。状態が隣としか行き来しないこの種の連鎖を birth-death 過程と呼びます。
- 上りの遷移率(birth・到着):どの状態 からも
- 下りの遷移率(death・サービス完了):状態 から (窓口が1個動いている)
flowchart LR S0["0"] -->|"λ"| S1["1"] S1 -->|"μ"| S0 S1 -->|"λ"| S2["2"] S2 -->|"μ"| S1 S2 -->|"λ"| S3["3"] S3 -->|"μ"| S2 S3 -->|"λ"| Sd["…"] Sd -->|"μ"| S3
2. 流量の釣り合いから定常分布 を導く
定常分布 を求めます。birth-death 過程では、隣り合う状態の境界線を横切る流れが、定常では上下で釣り合う(詳細釣り合い/cut balance)。これは 確率過程(マルコフ連鎖・ポアソン過程) の詳細釣り合い の連続時間版で、状態 と の間の「カット」を考えると:
なぜ釣り合うか。定常では状態 にいる確率が時間で変わらないので、 への流入と流出が等しい。境界をまたぐ流れは上り と下り の2本だけなので、この2本が等しくなります。ここから漸化式が出ます。
繰り返し適用すると、 で
最後に を規格化条件(全部足したら 1)で決めます。確率の総和は公比 の幾何級数です。
幾何級数 が収束するのは のときだけ――これが安定条件 の正体です。 なら和が発散し、 を正にとれない(定常分布が存在しない=行列が無限に伸びる)。こうして M/M/1 の定常分布が出ました。
これは公比 の幾何分布です。 は「窓口が空いている確率」、 は「窓口が塞がっている=到着客が待たされる確率」。一般に裾確率は
で、(待たされる確率)が再確認できます。
3. 定常分布から4つの指標を導く
が幾何分布だと分かれば、平均は機械的に出ます。系内数の期待値 は
ここで を使います(これは幾何級数 を で微分した に を掛けたもの)。代入して
残り3つはリトルの法則と分解(待ち行列の基礎とリトルの法則)で芋づる式に出ます。、、 を順に当てると:
( の変形は より 。 は を入れて 。)4指標が出そろいました。
4つすべてに が共通因子として効いている( は露わに、 は の形で)ことに注目してください。これが「 で全指標が発散する」数理的な理由です。
4. 閉形式 vs シミュレーション:定常分布まで一致する(コード)
導いた式を信じる前に、独立な離散事象シミュレーションで裏を取ります。Lindley 再帰でサービス開始・退去時刻を求め、平均だけでなく定常分布 そのもの(各人数 に滞在した時間の割合)が に一致するかまで見ます。平均が合うだけなら偶然もありえますが、分布全体が重なれば導出が正しい何よりの証拠です。()で回します。
import numpy as np
import pandas as pd
rng = np.random.default_rng(20260627)
lam = 0.75 # 到着率(人/分)
mu = 1.0 # サービス率(人/分)
rho = lam / mu
# --- 閉形式の M/M/1 ---
L_th = rho / (1 - rho)
Lq_th = rho**2 / (1 - rho)
W_th = 1 / (mu - lam)
Wq_th = rho / (mu - lam)
P_busy = rho # 待たされる確率 = サーバが塞がっている確率 = 1 - P_0
print(f"rho = lambda/mu = {rho:.4f}")
print(f"L = rho/(1-rho) = {L_th:.4f}")
print(f"Lq = rho^2/(1-rho) = {Lq_th:.4f}")
print(f"W = 1/(mu-lambda) = {W_th:.4f}")
print(f"Wq = rho/(mu-lambda) = {Wq_th:.4f}")
print(f"待たされる確率 P(N>=1) = rho = {P_busy:.4f}")
print()
# --- 離散事象シミュレーション(Lindley 再帰)---
N = 3_000_000
inter = rng.exponential(1.0 / lam, N)
arrivals = np.cumsum(inter)
service = rng.exponential(1.0 / mu, N)
cumS = np.cumsum(service)
dep = cumS + np.maximum.accumulate(arrivals - (cumS - service)) # 退去時刻
start = dep - service
wait_q = start - arrivals
sojourn = dep - arrivals
Wq_sim, W_sim = wait_q.mean(), sojourn.mean()
# 系内数 n(t) の時間平均 L と、各 n に滞在した時間割合(定常分布 P_n の実測)
t = np.concatenate([arrivals, dep])
sgn = np.concatenate([np.ones(N), -np.ones(N)])
order = np.argsort(t, kind="mergesort")
t, sgn = t[order], sgn[order]
level = np.cumsum(sgn).astype(int) # 各イベント直後の系内数
dt = np.diff(t)
lv = level[:-1] # 区間 [t_k, t_{k+1}] の系内数
horizon = t[-1] - t[0]
L_sim = float(np.sum(lv * dt) / horizon)
time_at = np.bincount(lv, weights=dt) # n ごとの滞在時間
P_emp = time_at / time_at.sum() # 実測 P_n
print(f"シミュ(N={N}):L={L_sim:.4f}(理論{L_th:.3f}) "
f"Wq={Wq_sim:.4f}(理論{Wq_th:.3f}) W={W_sim:.4f}(理論{W_th:.3f})")
print()
print("定常分布 P_n = (1-rho)*rho^n の検証(n=0..6)")
print(" n 理論P_n 実測P_n")
for n in range(7):
P_th = (1 - rho) * rho**n
print(f"{n:2d} {P_th:8.5f} {P_emp[n]:8.5f}")
出力:
rho = lambda/mu = 0.7500
L = rho/(1-rho) = 3.0000
Lq = rho^2/(1-rho) = 2.2500
W = 1/(mu-lambda) = 4.0000
Wq = rho/(mu-lambda) = 3.0000
待たされる確率 P(N>=1) = rho = 0.7500
シミュ(N=3000000):L=3.0140(理論3.000) Wq=3.0164(理論3.000) W=4.0177(理論4.000)
定常分布 P_n = (1-rho)*rho^n の検証(n=0..6)
n 理論P_n 実測P_n
0 0.25000 0.24886
1 0.18750 0.18739
2 0.14062 0.14042
3 0.10547 0.10558
4 0.07910 0.07917
5 0.05933 0.05932
6 0.04449 0.04459
出力の意味:閉形式は で 、待たされる確率 。シミュレーションの平均は で理論と小数第2位まで一致しました。決め手は下の表で、各人数に滞在した時間割合(実測 )が、幾何分布 (理論 )と から まで小数第3位までぴたり重なっています( vs 、 vs …)。平均だけでなく分布の形まで一致したことで、「流量の釣り合い → 」という導出が机上の空論でないと確認できました。、すなわち窓口は約25%の時間が空き・75%が稼働で、 の待たされる確率とも整合します。
5. の爆発と「余力を作る」効果(コード)
第3節で4指標すべてに が効くと分かりました。これが実務にどう響くかを2つの角度で見ます。(A) 到着 件/時を固定し、サービス率 を上げて余力を作ると待ちがどれだけ減るか。(B) が で発散する様子。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
# (A) 数値例:到着 lambda=9 件/時 を固定し、サービス率 mu を上げて「余力」を作る
# rho=lambda/mu が下がると Wq=rho/(mu-lambda) が激減することを表で示す
lam = 9.0 # 件/時
rows = []
for mu in [9.5, 10.0, 11.0, 12.0, 15.0]:
rho = lam / mu
Wq_hr = rho / (mu - lam) # 平均待ち時間(時)
L = rho / (1 - rho)
rows.append({"mu(件/時)": mu, "rho": rho, "Wq(分)": Wq_hr * 60, "L(系内数)": L})
df = pd.DataFrame(rows)
print(f"到着 lambda = {lam} 件/時(固定)。サービス率 mu を上げて余力を作る:")
print(df.to_string(index=False, float_format=lambda x: f"{x:.3f}"))
print()
# rho=0.8 -> 0.9 -> 0.95 で L がどう増えるか(倍々)
for rho in [0.80, 0.90, 0.95]:
print(f"rho={rho:.2f}: L=rho/(1-rho)={rho / (1 - rho):.2f}")
# (B) 図:L=rho/(1-rho) の 1/(1-rho) 爆発
rr = np.linspace(0.01, 0.97, 400)
L_curve = rr / (1 - rr)
plt.figure(figsize=(9, 5.5))
plt.plot(rr, L_curve, color="#1f77b4", lw=2, label=r"$L=\rho/(1-\rho)$")
for rho in [0.80, 0.90, 0.95]:
L = rho / (1 - rho)
plt.scatter([rho], [L], color="#d62728", zorder=5)
plt.annotate(f"ρ={rho:.2f}\nL={L:.0f}", (rho, L),
textcoords="offset points", xytext=(-38, -4), fontsize=9, color="#d62728")
plt.axvline(1.0, color="gray", ls="--", label=r"$\rho\to1$ で発散")
plt.xlabel("稼働率 ρ"); plt.ylabel("系内の平均人数 L")
plt.title(r"M/M/1:$L=\rho/(1-\rho)$ — 稼働率が1に近づくと待ちが発散する")
plt.ylim(0, 25); plt.legend(); plt.tight_layout()
plt.show()
出力:
到着 lambda = 9.0 件/時(固定)。サービス率 mu を上げて余力を作る:
mu(件/時) rho Wq(分) L(系内数)
9.500 0.947 113.684 18.000
10.000 0.900 54.000 9.000
11.000 0.818 24.545 4.500
12.000 0.750 15.000 3.000
15.000 0.600 6.000 1.500
rho=0.80: L=rho/(1-rho)=4.00
rho=0.90: L=rho/(1-rho)=9.00
rho=0.95: L=rho/(1-rho)=19.00
出力の意味:到着 9 件/時に対し、サービス能力が 件/時しかないと稼働率 0.947・待ち時間は約114分。ここで を上げて余力を作ると劇的に効きます—— で 54 分、 で 15 分。能力をたった (約26%増)に増やすだけで、待ち時間は 114分→15分(約8分の1) に縮みました。これは線形の改善ではありません。 を上げると分母の が大きくなると同時に も下がるので、 の効果が二重に効くからです。下段の も で と倍々で増えており、図の青い曲線が で天井知らずに跳ね上がる通りです。「混んできたら能力に少し余力を足す」だけで待ちが激減する――待ち行列設計の最も実用的な教訓です。
⚠️ よくある誤解
- 「平均待ち時間だけ見ればよい」ではない:M/M/1 の系内数は幾何分布 で裾を引きます。 なら もあり、「平均は9人でも、たまに非常に混む」。SLA は平均でなく**裾(95パーセンタイル待ち)**で切られることが多く、平均だけの設計は危険です。
- 「 が少し違っても待ちは少ししか変わらない」ではない: が効くので、 のわずかな差が待ちを大きく動かします( で待ち倍増)。需要予測や処理時間の見積もりが数%ずれただけで、待ち時間の予測は大きく外れます。高稼働域ほど見積もり精度がシビアです。
- 「M/M/1 はどんな窓口にも当てはまる」ではない:M/M/1 は無限母集団・FIFO・指数サービス・割り込みなし・1窓口という理想化です。サービス時間が指数でない(ばらつきが小さい)なら、同じ でも待ちは M/M/1 より短くなります(一般には Pollaczek–Khinchine 公式で がサービス時間の変動係数に依存。要最新確認)。M/M/1 は「指数という最も暴れるサービス」での上限の目安と捉えるのが安全です。
- 「窓口を増やすのと能力を上げるのは同じ」ではない: を上げる(1窓口を速くする)のと、窓口数 を増やすのは別の設計で、待ちへの効き方も違います。複数窓口(M/M/c)と要員設計は次章 M/M/c 待ち行列と窓口設計 で扱います。
関連ノート
- 待ち行列の基礎とリトルの法則(前提・指標と 。本ノートで絶対値を出した)
- M/M/c 待ち行列と窓口設計(次のトピック・窓口 個への一般化・アーランC・プーリング)
- 確率過程(マルコフ連鎖・ポアソン過程)(統計・連続時間マルコフ/詳細釣り合い の一般論。本ノートはその M/M/1 への適用)
- 指数分布・ガンマ分布・ベータ分布(統計・指数分布の無記憶性。マルコフ性の根拠)
- オペレーションズ・マネジメント 全体目次