Mímisbrunnr知恵の泉

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

🎓 レベル:応用 | 重要度:A(必須)

📎 前提:M/M/1 待ち行列モデルWq=ρ/(μλ)W_q=\rho/(\mu-\lambda))・待ち行列の基礎とリトルの法則(稼働率と待ちの非線形) | 在庫バッファ:安全在庫と発注点 | 締め:制約理論TOCとスループットリーン生産・JIT・かんばん

要点(BLUF)

1. なぜリーンとTOCが効くのか:ばらつきがフローを壊す

待ち行列の基礎とリトルの法則 で、稼働率 ρ\rho を1に近づけると待ちが 1/(1ρ)1/(1-\rho) で爆発するのを見ました。M/M/1 ではこれが厳密に出ましたが、現実の到着もサービスも指数分布とは限りません。では何が待ちを決めるのか。答えは「ばらつきと稼働率」です。

直感はこうです。もし到着もサービスもまったくばらつかない(一定間隔で来て、一定時間で終わる)なら、能力が足りる限り待ちはゼロにできます——客が来るたびに窓口がちょうど空いている。待ちが生まれるのは、到着が固まったり、サービスが長引いたりするばらつきのせい。そして稼働率が高いほど、一度生じた行列が解消されにくい。だから待ち=ばらつき問題であり、ばらつきを下げればフローは改善する。これがリーン(平準化・一個流し・品質の作り込み)と TOC(バッファで制約を守る)が共通して効く根っこです。

ばらつきの大きさは変動係数で測ります。標準偏差を平均で割った CV=σ/meanCV=\sigma/\text{mean}、その2乗が SCV(squared coefficient of variation)

c2=σ2mean2c^2=\frac{\sigma^2}{\text{mean}^2}

SCV は無次元で、分布の「暴れ具合」を平均で正規化した量。指数分布は σ=mean\sigma=\text{mean} なので c2=1c^2=1、一定(決定的)なら c2=0c^2=0、指数より暴れる(バースト的)なら c2>1c^2>1この c2c^2 がそのまま待ち時間に効きます

2. キングマンの式とM/M/1への帰着

単一窓口の一般待ち行列 G/G/1(到着もサービスも一般分布)の平均待ち時間 WqW_q について、キングマン(Kingman, 1961)の近似式が成り立ちます。

Wqca2+cs22V:ばらつきρ1ρU:稼働率teT:時間W_q\approx\underbrace{\frac{c_a^2+c_s^2}{2}}_{V:\text{ばらつき}}\cdot\underbrace{\frac{\rho}{1-\rho}}_{U:\text{稼働率}}\cdot\underbrace{t_e}_{T:\text{時間}}

ここで ca2c_a^2 は到着間隔の SCV、cs2c_s^2 はサービス時間の SCV、ρ\rho は稼働率、te=1/μt_e=1/\mu は平均サービス時間(effective process time)。ファクトリーフィジクス(Hopp & Spearman)はこれを VUT 分解と呼びます。

3つの積だというのが教訓です。VV を半分にすれば WqW_q も半分。ρ\rho を下げれば UU が下がる。ばらつき・稼働率・処理時間のどれを攻めても待ちは減る——が、UUρ1\rho\to1 で発散するので、高稼働域では VV を下げるのが効きます。

M/M/1 への帰着を確かめましょう。ポアソン到着は到着間隔が指数分布なので ca2=1c_a^2=1、指数サービスも cs2=1c_s^2=1。代入すると V=(1+1)/2=1V=(1+1)/2=1 で、

Wq1ρ1ρte=ρte1ρ=ρμλW_q\approx 1\cdot\frac{\rho}{1-\rho}\cdot t_e=\frac{\rho\,t_e}{1-\rho}=\frac{\rho}{\mu-\lambda}

これは M/M/1 待ち行列モデル で導いた M/M/1 の WqW_q完全に一致します。実はキングマンの式は、ポアソン到着(ca2=1c_a^2=1)なら M/M/1・M/G/1 で厳密(M/G/1 はポラチェック–ヒンチン公式と一致)、一般の G/G/1 では重交通(ρ1\rho\to1)で漸近的に厳密になる近似です。「M/M/1 は ca2=cs2=1c_a^2=c_s^2=1 という特別な1点」で、キングマンはそこからばらつきを動かせるように一般化したもの、と捉えてください。

3. バッファリング法則:在庫・能力・時間

キングマンの式は、ばらつきは消えてなくならず、必ず何かで「払う」ことを教えます。V>0V>0 である限り Wq>0W_q>0。この支払いの形は3通りしかありません(Hopp & Spearman のバッファリング法則)。

flowchart LR
  VAR["ばらつき<br/>(到着 ca・サービス cs)"] --> BUF{"必ずどれかで吸収"}
  BUF --> INV["在庫バッファ<br/>(安全在庫・WIP)"]
  BUF --> CAP["能力バッファ<br/>(余力=稼働率を下げる)"]
  BUF --> TIME["時間バッファ<br/>(待ち時間・リードタイム)"]

3つはトレードオフで、どれかを減らせば別のどれかが増えます。在庫を削れば(リーンでかんばんを絞れば)、同じばらつきなら能力か時間で払うしかない——だからばらつきを下げずに在庫だけ削ると、欠品(時間バッファの破綻)や残業(能力バッファ)にしわ寄せが行きます。リーンの本質は「在庫を削る」ことではなく、VVca,csc_a,c_s)そのものを下げて、3つのバッファの総量を減らすことなのです。次のコードで、VV を下げると「同じ待ち(時間バッファ一定)でより高い稼働率(能力バッファを薄く)」が許されることを見ます。

4. キングマンの式とG/G/1シミュレーション(コード)

キングマンの式を、(1) ca2=cs2=1c_a^2=c_s^2=1 で M/M/1 に一致すること、(2) ばらつき(SCV)を下げると WqW_q が減ること、(3) ガンマ分布で SCV を指定した G/G/1 の離散事象シミュレーションで裏取りすること、の3段で確かめます。シミュレーションは待ち時間の Lindley 再帰 Wq,i=max(0, Wq,i1+Si1Ai)W_{q,i}=\max(0,\ W_{q,i-1}+S_{i-1}-A_i)待ち行列の基礎とリトルの法則 と同じ)。ガンマ分布は平均 mm・SCV c2c^2shape =1/c2=1/c^2・scale =mc2=m\,c^2 で指定でき、c2=1c^2=1 で指数分布になります。

import numpy as np
import pandas as pd

def kingman_wq(ca2, cs2, rho, te):
    """キングマン近似 Wq = (ca^2+cs^2)/2 * rho/(1-rho) * te。"""
    return (ca2 + cs2) / 2.0 * rho / (1.0 - rho) * te

te = 1.0   # 平均サービス時間 te = 1/mu(mu=1)
print("=== ca^2=cs^2=1 のとき キングマン式 = M/M/1 の Wq ===")
print(" rho    Kingman_Wq   MM1_Wq=rho*te/(1-rho)")
for rho in [0.50, 0.70, 0.80, 0.90, 0.95]:
    kq = kingman_wq(1.0, 1.0, rho, te)
    mm1 = rho / (1.0 - rho) * te
    print(f"{rho:.2f}    {kq:9.4f}    {mm1:9.4f}")

print()
print("=== rho=0.90 固定:ばらつき(SCV)を下げると待ちが減る ===")
print(" ca^2  cs^2   Kingman_Wq")
for ca2, cs2 in [(2.0, 2.0), (1.0, 1.0), (0.5, 0.5), (0.25, 0.25), (0.0, 0.0)]:
    print(f"{ca2:.2f}  {cs2:.2f}   {kingman_wq(ca2, cs2, 0.90, te):8.4f}")

def simulate_gg1(lam, mu, ca2, cs2, n, seed):
    """G/G/1 を Lindley 再帰 Wq_i=max(0, Wq_{i-1}+S_{i-1}-A_i) でシミュ。
    到着間隔・サービスはガンマ(平均 m, SCV c^2 -> shape=1/c^2, scale=m*c^2)。"""
    rng = np.random.default_rng(seed)
    A = rng.gamma(1.0 / ca2, (1.0 / lam) * ca2, n)   # 到着間隔(平均 1/lam)
    S = rng.gamma(1.0 / cs2, (1.0 / mu) * cs2, n)    # サービス時間(平均 1/mu)
    Wq = 0.0
    tot = 0.0
    warm = n // 10
    for i in range(1, n):
        Wq = max(0.0, Wq + S[i - 1] - A[i])
        if i >= warm:
            tot += Wq
    return tot / (n - warm)

print()
print("=== G/G/1 シミュでキングマン近似を裏取り(mu=1, n=1,000,000)===")
print(" rho   ca^2  cs^2   Kingman   simulation")
n = 1_000_000
for rho, ca2, cs2 in [(0.90, 1.0, 1.0), (0.90, 0.5, 0.5), (0.90, 2.0, 2.0),
                      (0.95, 0.25, 0.25), (0.80, 1.0, 1.0)]:
    kq = kingman_wq(ca2, cs2, rho, te)
    sim = simulate_gg1(rho, 1.0, ca2, cs2, n, seed=20260627)
    print(f"{rho:.2f}   {ca2:.2f}  {cs2:.2f}   {kq:7.4f}   {sim:9.4f}")

出力:

=== ca^2=cs^2=1 のとき キングマン式 = M/M/1 の Wq ===
 rho    Kingman_Wq   MM1_Wq=rho*te/(1-rho)
0.50       1.0000       1.0000
0.70       2.3333       2.3333
0.80       4.0000       4.0000
0.90       9.0000       9.0000
0.95      19.0000      19.0000

=== rho=0.90 固定:ばらつき(SCV)を下げると待ちが減る ===
 ca^2  cs^2   Kingman_Wq
2.00  2.00    18.0000
1.00  1.00     9.0000
0.50  0.50     4.5000
0.25  0.25     2.2500
0.00  0.00     0.0000

=== G/G/1 シミュでキングマン近似を裏取り(mu=1, n=1,000,000)===
 rho   ca^2  cs^2   Kingman   simulation
0.90   1.00  1.00    9.0000      8.9813
0.90   0.50  0.50    4.5000      4.2958
0.90   2.00  2.00   18.0000     18.5598
0.95   0.25  0.25    4.7500      4.5210
0.80   1.00  1.00    4.0000      4.0131

出力の意味:上段で、ca2=cs2=1c_a^2=c_s^2=1 のキングマン式は M/M/1 の Wq=ρte/(1ρ)W_q=\rho t_e/(1-\rho) と全 ρ\rho で完全一致ρ=0.9\rho=0.9 で 9.0、ρ=0.95\rho=0.95 で 19.0)。キングマンが M/M/1 の一般化だと確認できます。中段、ρ=0.9\rho=0.9 に固定してばらつきだけを下げると、WqW_qSCV=2=2 の18.0 → SCV=1=1 の9.0 → SCV=0.5=0.5 の4.5 → SCV=0.25=0.25 の2.25 → SCV=0=0(決定的)の0.0 へ。稼働率も処理時間も一切変えず、ばらつきを下げるだけで待ちが消えていきますVV に比例)。下段の G/G/1 シミュレーションでは、ガンマ分布で SCV を指定して Lindley 再帰を100万回回した実測 WqW_q が、キングマン式と突き合わさります——ca2=1c_a^2=1 の行(M/M/1 相当の (1,1) と (1,1))は 8.9813 vs 9.0、4.0131 vs 4.0 とほぼ厳密一致(ポアソン到着では式が厳密)。ca21c_a^2\neq1 の行((0.5,0.5)・(2,2)・(0.25,0.25))は 4.30 vs 4.5、18.56 vs 18.0、4.52 vs 4.75 と数%以内で、キングマンが良い近似であること(誤差は非ポアソン到着ぶん。重交通 ρ1\rho\to1 ほど近似は良くなる)を示しています。

5. ばらつき低減が高稼働を安全にする(コード)

バッファリング法則の核心——「ばらつきを下げると、同じ待ち(時間バッファ一定)で、より高い稼働率(能力バッファを薄く)が許される」を可視化します。目標待ち時間を Wq=9W_q=9tet_e の9倍)に固定し、ばらつき水準ごとに WqW_q-ρ\rho 曲線を描いて、どこまで稼働率を上げてよいか ρ\rho^\ast を読み取ります。ca2+cs22ρ1ρte=Wq目標\dfrac{c_a^2+c_s^2}{2}\cdot\dfrac{\rho}{1-\rho}\cdot t_e=W_q^{\text{目標}}ρ\rho について解くと ρ=Wq目標Wq目標+Vte\rho^\ast=\dfrac{W_q^{\text{目標}}}{W_q^{\text{目標}}+V\,t_e} です。

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib

te = 1.0
def wq(ca2, cs2, rho, te):
    return (ca2 + cs2) / 2.0 * rho / (1.0 - rho) * te

target = 9.0   # 目標待ち時間(= te の9倍)
print("目標 Wq =", target, "を同じ待ちに保つとき、各ばらつき水準が許す稼働率 rho*")
print(" SCV(=ca^2=cs^2)   許される rho*   = target/(target+SCV)")
for scv in [2.0, 1.0, 0.5, 0.25]:
    rho_star = target / (target + scv)    # (ca2+cs2)/2 * r/(1-r) = target を解く
    print(f"   {scv:.2f}            {rho_star:.4f}")

rho = np.linspace(0.01, 0.975, 400)
plt.figure(figsize=(9, 5.5))
curves = [(2.0, "高ばらつき SCV=2", "#d62728"),
          (1.0, "中 SCV=1(M/M/1)", "#ff7f0e"),
          (0.25, "低ばらつき SCV=0.25(リーン)", "#1f77b4")]
for scv, label, color in curves:
    plt.plot(rho, wq(scv, scv, rho, te), color=color, lw=2, label=label)
    r_star = target / (target + scv)
    plt.scatter([r_star], [target], color=color, zorder=5)
    plt.annotate(f"rho*={r_star:.3f}", (r_star, target),
                 textcoords="offset points", xytext=(4, 6), fontsize=9, color=color)
plt.axhline(target, ls=":", color="gray", label=f"目標 Wq={target:.0f}")
plt.xlabel("稼働率 rho"); plt.ylabel("平均待ち時間 Wq")
plt.title("ばらつき低減(リーン)は Wq-rho 曲線を下へずらす=同じ待ちでより高稼働を許す")
plt.ylim(0, 30); plt.xlim(0, 1.0)
plt.legend(loc="upper left"); plt.tight_layout()
plt.show()

出力:

目標 Wq = 9.0 を同じ待ちに保つとき、各ばらつき水準が許す稼働率 rho*
 SCV(=ca^2=cs^2)   許される rho*   = target/(target+SCV)
   2.00            0.8182
   1.00            0.9000
   0.50            0.9474
   0.25            0.9730

出力の意味:同じ目標待ち時間 Wq=9W_q=9 を守るのに、高ばらつき(SCV=2=2)の現場では稼働率を ρ=0.818\rho^\ast=0.818 までしか上げられません。ばらつきを SCV=1=1(M/M/1 並み)に下げれば 0.9000.900、さらにリーンで SCV=0.25=0.25 まで下げれば 0.9730.973 まで稼働率を上げてよい。図では、ばらつきを下げるほど WqW_q-ρ\rho 曲線が下へずれ、目標線(Wq=9W_q=9)と交わる点(ρ\rho^\ast)が右へ動きます。これがバッファリング法則の実践的含意です——ばらつきを下げる(時間バッファも在庫バッファも据え置き)と、能力バッファを薄くできる=同じサービス水準でより高い稼働率で回せる。高ばらつきのまま稼働率0.97で回せば待ちは天文学的(V=2V=2 なら Wq=2×0.97/0.0365W_q=2\times0.97/0.03\approx65)ですが、ばらつきを下げてあれば0.97でも Wq=9W_q=9 で済む。「稼働率を上げてよいかどうか」は、ばらつきを下げてからでないと決められないのです。リーンが在庫削減の前にまず平準化・段取り短縮・品質作り込みで ca,csc_a,c_s を攻めるのは、この順序を守るためです。

⚠️ よくある誤解

関連ノート