Mímisbrunnr知恵の泉

← オペレーションズマネジメント 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:待ち行列の基礎とリトルの法則(指標 L,Lq,W,Wq・稼働率 ρ・L=λW) | 確率の土台:確率過程(マルコフ連鎖・ポアソン過程)(連続時間マルコフ・詳細釣り合い)・指数分布・ガンマ分布・ベータ分布(指数の無記憶性) | 次:M/M/c 待ち行列と窓口設計

要点(BLUF)

1. M/M/1 を birth-death 過程として立てる

M/M/1 の系内数 N(t)N(t)(サービス中も含めた人数)を状態とします。M(指数・無記憶)の威力はここで効きます——到着間隔も残りサービス時間も無記憶(指数分布・ガンマ分布・ベータ分布)なので、「いま何人いるか」さえ分かれば、過去の経緯(誰がいつ来たか)を一切知らなくても将来の確率が決まります。つまり N(t)N(t)連続時間マルコフ連鎖です。

しかも微小時間 dtdt の間に起きうるのは「到着1件(nn+1n\to n+1)」か「サービス完了1件(nn1n\to n-1)」だけで、2件同時はほぼ起きません(ポアソン過程の希少性、確率過程(マルコフ連鎖・ポアソン過程))。状態が隣としか行き来しないこの種の連鎖を birth-death 過程と呼びます。

flowchart LR
  S0["0"] -->|"λ"| S1["1"]
  S1 -->|"μ"| S0
  S1 -->|"λ"| S2["2"]
  S2 -->|"μ"| S1
  S2 -->|"λ"| S3["3"]
  S3 -->|"μ"| S2
  S3 -->|"λ"| Sd["…"]
  Sd -->|"μ"| S3

2. 流量の釣り合いから定常分布 Pn=(1ρ)ρnP_n=(1-\rho)\rho^n を導く

定常分布 Pn=Pr(N=で系内がn)P_n=\Pr(N=\infty\text{で系内が}n) を求めます。birth-death 過程では、隣り合う状態の境界線を横切る流れが、定常では上下で釣り合う(詳細釣り合い/cut balance)。これは 確率過程(マルコフ連鎖・ポアソン過程) の詳細釣り合い πiPij=πjPji\pi_i P_{ij}=\pi_j P_{ji} の連続時間版で、状態 nnn+1n+1 の間の「カット」を考えると:

λPnnn+1 の上り流量=μPn+1n+1n の下り流量(n=0,1,2,)\underbrace{\lambda P_n}_{n\to n+1\text{ の上り流量}}=\underbrace{\mu P_{n+1}}_{n+1\to n\text{ の下り流量}}\qquad(n=0,1,2,\dots)

なぜ釣り合うか。定常では状態 n+1n+1 にいる確率が時間で変わらないので、n+1n+1 への流入と流出が等しい。境界をまたぐ流れは上り λPn\lambda P_n と下り μPn+1\mu P_{n+1} の2本だけなので、この2本が等しくなります。ここから漸化式が出ます。

Pn+1=λμPn=ρPn(ρ:=λμ)P_{n+1}=\frac{\lambda}{\mu}P_n=\rho\,P_n\qquad\Bigl(\rho:=\frac{\lambda}{\mu}\Bigr)

繰り返し適用すると、P1=ρP0, P2=ρP1=ρ2P0,P_1=\rho P_0,\ P_2=\rho P_1=\rho^2 P_0,\dots

Pn=ρnP0P_n=\rho^n P_0

最後に P0P_0規格化条件(全部足したら 1)で決めます。確率の総和は公比 ρ\rho の幾何級数です。

n=0Pn=P0n=0ρn=P01ρ=1P0=1ρ\sum_{n=0}^{\infty}P_n=P_0\sum_{n=0}^{\infty}\rho^n=\frac{P_0}{1-\rho}=1 \quad\Longrightarrow\quad P_0=1-\rho

幾何級数 n0ρn=11ρ\sum_{n\ge0}\rho^n=\dfrac{1}{1-\rho}収束するのは ρ<1\rho<1 のときだけ――これが安定条件 ρ<1\rho<1 の正体です。ρ1\rho\ge1 なら和が発散し、P0P_0 を正にとれない(定常分布が存在しない=行列が無限に伸びる)。こうして M/M/1 の定常分布が出ました。

Pn=(1ρ)ρn(n=0,1,2,)\boxed{\,P_n=(1-\rho)\,\rho^n\,}\qquad(n=0,1,2,\dots)

これは公比 ρ\rho の幾何分布です。P0=1ρP_0=1-\rho は「窓口が空いている確率」、1P0=ρ1-P_0=\rho は「窓口が塞がっている=到着客が待たされる確率」。一般に裾確率

P(Nn)=kn(1ρ)ρk=ρnP(N\ge n)=\sum_{k\ge n}(1-\rho)\rho^k=\rho^n

で、P(N1)=ρP(N\ge1)=\rho(待たされる確率)が再確認できます。

3. 定常分布から4つの指標を導く

PnP_n が幾何分布だと分かれば、平均は機械的に出ます。系内数の期待値 LL

L=n=0nPn=(1ρ)n=0nρnL=\sum_{n=0}^{\infty}n\,P_n=(1-\rho)\sum_{n=0}^{\infty}n\,\rho^n

ここで n0nρn=ρ(1ρ)2\sum_{n\ge0}n\rho^n=\dfrac{\rho}{(1-\rho)^2} を使います(これは幾何級数 ρn=11ρ\sum\rho^n=\frac{1}{1-\rho}ρ\rho で微分した nρn1=1(1ρ)2\sum n\rho^{n-1}=\frac{1}{(1-\rho)^2}ρ\rho を掛けたもの)。代入して

L=(1ρ)ρ(1ρ)2=ρ1ρ\boxed{\,L=(1-\rho)\cdot\frac{\rho}{(1-\rho)^2}=\frac{\rho}{1-\rho}\,}

残り3つはリトルの法則と分解(待ち行列の基礎とリトルの法則)で芋づる式に出ます。W=L/λW=L/\lambdaWq=W1/μW_q=W-1/\muLq=λWqL_q=\lambda W_q を順に当てると:

W=Lλ=ρλ(1ρ)=1μλ,Wq=W1μ=1μλ1μ=ρμλW=\frac{L}{\lambda}=\frac{\rho}{\lambda(1-\rho)}=\frac{1}{\mu-\lambda},\qquad W_q=W-\frac{1}{\mu}=\frac{1}{\mu-\lambda}-\frac{1}{\mu}=\frac{\rho}{\mu-\lambda} Lq=λWq=λρμλ=ρ21ρL_q=\lambda W_q=\frac{\lambda\rho}{\mu-\lambda}=\frac{\rho^2}{1-\rho}

WW の変形は ρ=λ/μ\rho=\lambda/\mu より ρλ(1ρ)=1μ(1ρ)=1μλ\dfrac{\rho}{\lambda(1-\rho)}=\dfrac{1}{\mu(1-\rho)}=\dfrac{1}{\mu-\lambda}LqL_qλ=ρμ\lambda=\rho\mu を入れて ρμρμλ=ρ21ρ\dfrac{\rho\mu\cdot\rho}{\mu-\lambda}=\dfrac{\rho^2}{1-\rho}。)4指標が出そろいました。

L=ρ1ρ,Lq=ρ21ρ,W=1μλ,Wq=ρμλL=\frac{\rho}{1-\rho},\quad L_q=\frac{\rho^2}{1-\rho},\quad W=\frac{1}{\mu-\lambda},\quad W_q=\frac{\rho}{\mu-\lambda}

4つすべてに 11ρ\dfrac{1}{1-\rho} が共通因子として効いている(L,LqL,L_q は露わに、W,WqW,W_qμλ=μ(1ρ)\mu-\lambda=\mu(1-\rho) の形で)ことに注目してください。これが「ρ1\rho\to1 で全指標が発散する」数理的な理由です。

4. 閉形式 vs シミュレーション:定常分布まで一致する(コード)

導いた式を信じる前に、独立な離散事象シミュレーションで裏を取ります。Lindley 再帰でサービス開始・退去時刻を求め、平均だけでなく定常分布 PnP_n そのもの(各人数 nn に滞在した時間の割合)が (1ρ)ρn(1-\rho)\rho^n に一致するかまで見ます。平均が合うだけなら偶然もありえますが、分布全体が重なれば導出が正しい何よりの証拠です。ρ=0.75\rho=0.75λ=0.75,μ=1\lambda=0.75,\mu=1)で回します。

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

出力の意味:閉形式は ρ=0.75\rho=0.75L=3, Lq=2.25, W=4, Wq=3L=3,\ L_q=2.25,\ W=4,\ W_q=3、待たされる確率 ρ=0.75\rho=0.75。シミュレーションの平均は L=3.014, Wq=3.016, W=4.018L=3.014,\ W_q=3.016,\ W=4.018理論と小数第2位まで一致しました。決め手は下の表で、各人数に滞在した時間割合(実測 PnP_n)が、幾何分布 (1ρ)ρn(1-\rho)\rho^n(理論 PnP_n)と n=0n=0 から 66 まで小数第3位までぴたり重なっています(P0=0.249P_0=0.249 vs 0.2500.250P3=0.1056P_3=0.1056 vs 0.10550.1055…)。平均だけでなく分布の形まで一致したことで、「流量の釣り合い → Pn=(1ρ)ρnP_n=(1-\rho)\rho^n」という導出が机上の空論でないと確認できました。P00.25P_0\approx0.25、すなわち窓口は約25%の時間が空き・75%が稼働で、P(N1)=ρ=0.75P(N\ge1)=\rho=0.75 の待たされる確率とも整合します。

5. 1/(1ρ)1/(1-\rho) の爆発と「余力を作る」効果(コード)

第3節で4指標すべてに 1/(1ρ)1/(1-\rho) が効くと分かりました。これが実務にどう響くかを2つの角度で見ます。(A) 到着 λ=9\lambda=9 件/時を固定し、サービス率 μ\mu を上げて余力を作ると待ちがどれだけ減るか。(B) L=ρ/(1ρ)L=\rho/(1-\rho)ρ1\rho\to1 で発散する様子。

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 件/時に対し、サービス能力が μ=9.5\mu=9.5 件/時しかないと稼働率 0.947・待ち時間は約114分。ここで μ\mu を上げて余力を作ると劇的に効きます——μ=10\mu=10 で 54 分、μ=12\mu=1215 分。能力をたった 9.5129.5\to12(約26%増)に増やすだけで、待ち時間は 114分→15分(約8分の1) に縮みました。これは線形の改善ではありません。μ\mu を上げると分母の μλ\mu-\lambda が大きくなると同時に ρ\rho も下がるので、1/(1ρ)1/(1-\rho) の効果が二重に効くからです。下段の LLρ=0.80.90.95\rho=0.8\to0.9\to0.9549194\to9\to19倍々で増えており、図の青い曲線が ρ=1\rho=1 で天井知らずに跳ね上がる通りです。「混んできたら能力に少し余力を足す」だけで待ちが激減する――待ち行列設計の最も実用的な教訓です。

⚠️ よくある誤解

関連ノート