🎓 レベル:基礎 | 重要度:A(必須)
📎 前提:オペレーションズ・マネジメントとは(L=λW の導入) | 次:ボトルネックとキャパシティ | 数理:確率過程(マルコフ連鎖・ポアソン過程)
要点(BLUF)
- プロセスは3つの指標で読みます——在庫 (仕掛・WIP)、フローレート (スループット)、フロータイム (1ユニットが系にいる時間)。
- この3つを結ぶ中心の恒等式が リトルの法則 。これは オペレーションズ・マネジメントとは の とまったく同じ式で、語彙が「在庫・プロセス」になっただけです。
- フロータイム効率 = 付加価値時間 / 総フロータイム は、実プロセスではしばしば数 % しかありません。残りは待ち=改善余地です。
- リトルの法則は滞在時間の分布によらず成り立ちます。本稿ではそれを擬似データで実証します(待ち行列の理論そのものは第4章・確率過程(マルコフ連鎖・ポアソン過程))。
1. プロセスを3つの指標で見る
OM では、製品や注文や患者など「系の中を流れる対象」をフローユニットと呼びます。プロセスの状態は、次の3つの指標で過不足なく記述できます。
- 在庫 (inventory / WIP):いまこの瞬間、系の中にいるフローユニットの平均数(仕掛品、待っている注文、店内の客)。
- フローレート (flow rate / throughput):単位時間に系を通り抜ける数(個/分、件/時)。
- フロータイム (flow time):1つのフローユニットが入口から出口まで系の中で過ごす平均時間。
この3つは独立ではなく、たった1本の式で結ばれています。それがリトルの法則です。01-01 で導入した待ち行列の記号との対応は次の通り——同じ法則の言い換えです。
| 記号 | プロセス分析の呼び名 | 待ち行列(01-01) | 意味 |
|---|---|---|---|
| 在庫・仕掛 WIP | (系内数) | 系の中の平均数 | |
| フローレート・スループット | (到着率) | 単位時間に流れる数 | |
| フロータイム | (滞在時間) | 1ユニットが系にいる時間 |
2. リトルの法則 を導く
なぜこの式が成り立つのか、確率分布をいっさい仮定せずに導けます(これが「分布によらない」ことの理由です)。観測時間 のあいだに 個のフローユニットが系を通り、ユニット の滞在時間を とします。系内数 (時刻 に系の中にいる数)を時間積分すると、各ユニットは自分が系にいた時間ぶんだけ面積に寄与するので、
両辺を で割ります。左辺は系内数の時間平均=在庫 。右辺は と分解でき、 がフローレート 、 がフロータイム です。よって
確率も分布も登場しません。定常(入る量と出る量が長期的に釣り合う)でありさえすれば成り立つ恒等式です。これが在庫管理(第3章)でも待ち行列(第4章)でも同じ式が効く理由です。
3. プロセス分析の実際:フロータイム効率(コード)
1件の注文が複数アクティビティを通る工程を考えます。各アクティビティには付加価値時間(実際に価値を生む作業)と、その手前で生じる待ち時間(滞留)があります。総フロータイムに占める付加価値時間の割合がフロータイム効率です。
flowchart LR A["受注入力"] --> B["与信審査"] --> C["在庫引当"] --> D["ピッキング"] --> E["梱包"] --> F["出荷検品"]
import numpy as np
import pandas as pd
# あるフローユニット(1件の受注)が通る工程。各アクティビティの
# 付加価値時間(実作業)と、その前後の待ち時間(滞留)を分けて記録(分)
proc = pd.DataFrame({
"アクティビティ": ["受注入力", "与信審査", "在庫引当", "ピッキング", "梱包", "出荷検品"],
"付加価値時間": [5, 10, 3, 12, 8, 4], # 分(実作業)
"待ち時間": [0, 120, 30, 45, 15, 20], # 分(前工程での滞留)
})
proc["工程内時間"] = proc["付加価値時間"] + proc["待ち時間"]
T_va = proc["付加価値時間"].sum() # 付加価値時間の合計
T_flow = proc["工程内時間"].sum() # 総フロータイム T
eff = T_va / T_flow # フロータイム効率
print(proc.to_string(index=False))
print()
print(f"付加価値時間 T_VA = {T_va} 分")
print(f"総フロータイム T = {T_flow} 分 (= {T_flow/60:.3f} 時間)")
print(f"フロータイム効率 = {eff:.3f} ({eff*100:.1f} %)")
# リトルの法則 I = R*T:スループット R を与えて仕掛在庫 WIP を算出
R = 30.0 # 件/時(このプロセスを流れるフローレート)
T_hours = T_flow / 60 # 総フロータイムを時間に換算
I = R * T_hours # I = R × T(件/時 × 時 = 件)
print()
print(f"スループット R = {R} 件/時")
print(f"WIP I = R*T = {I:.1f} 件")
出力:
アクティビティ 付加価値時間 待ち時間 工程内時間
受注入力 5 0 5
与信審査 10 120 130
在庫引当 3 30 33
ピッキング 12 45 57
梱包 8 15 23
出荷検品 4 20 24
付加価値時間 T_VA = 42 分
総フロータイム T = 272 分 (= 4.533 時間)
フロータイム効率 = 0.154 (15.4 %)
スループット R = 30.0 件/時
WIP I = R*T = 136.0 件
出力の意味:272 分のフロータイムのうち、実際に価値を生んでいるのは 42 分だけ。フロータイム効率は 15.4 % で、残り 84.6 % は待ち(とくに与信審査の前で 120 分も滞留)です。改善の的は作業の高速化ではなく、この滞留の削減だと一目でわかります。最後にリトルの法則 を使い、フローレート 件/時・フロータイム 時から、この工程に常時 136 件が仕掛として滞留していると算出しました。フロータイムを縮めれば、 が同じでも仕掛在庫がそのぶん減ります。
4. リトルの法則は分布によらない(実証コード)
導出(第2節)が示す通り、 は確率分布を仮定しない恒等式です。これを擬似データで確かめます。到着はポアソン過程(到着間隔 ~ Exp)にしつつ、各ユニットの滞在時間 はあえて非指数——右に重く歪んだ対数正規・ガンマ・一定(決定的)——から生成します。時間軸上の系内数 の階段関数から時間平均 を直接計算し、 と一致するかを見ます。
これは「待ち行列の理論」ではなく恒等式の実証です。到着・サービスの分布から平均待ち時間を求める理論は第4章と 確率過程(マルコフ連鎖・ポアソン過程) で扱います。
import numpy as np
import pandas as pd
rng = np.random.default_rng(7)
lam = 3.0 # 平均到着率(件/時)
horizon = 20000.0 # 観測時間(時)
mu_T = 2.0 # どの分布でも平均滞在時間は 2.0 時間にそろえる
# 到着=ポアソン過程(到着間隔 ~ Exp(1/λ))。観測窓 [0, horizon) 内の到着のみ
gaps = rng.exponential(1.0/lam, size=int(lam*horizon*1.2))
arrivals = np.cumsum(gaps)
arrivals = arrivals[arrivals < horizon]
N = arrivals.size
def time_average_L(arrivals, T, horizon):
"""系内数 n(t) の階段関数(区分定数)から時間平均 L を直接計算する。"""
departures = arrivals + T
ev_t = np.concatenate([arrivals, departures]) # イベント時刻
ev_d = np.concatenate([np.ones(arrivals.size), -np.ones(arrivals.size)]) # 到着+1/退去-1
order = np.argsort(ev_t, kind="mergesort")
ev_t, ev_d = ev_t[order], ev_d[order]
level = np.cumsum(ev_d) # 各イベント直後の系内数
tc = np.clip(ev_t, 0.0, horizon) # 観測窓にクリップ
widths = np.diff(tc) # 各区間の幅 dt
area = float(np.sum(level[:-1] * widths)) # Σ 系内数 × dt(区分定数の積分)
return area / horizon
# 「あえて非指数」の滞在時間分布。平均はすべて mu_T にそろえる
dists = {
"lognormal": lambda n: rng.lognormal(np.log(mu_T) - 0.5, 1.0, n), # 右に重い裾
"gamma(k=2)": lambda n: rng.gamma(2.0, mu_T / 2.0, n), # CV=0.707
"deterministic": lambda n: np.full(n, mu_T), # 一定(CV=0)
}
rows = []
for name, sampler in dists.items():
T = sampler(N)
W = T.mean() # 平均滞在時間
cv = T.std() / T.mean() # 変動係数(指数分布なら 1)
L_sim = time_average_L(arrivals, T, horizon)
lamW = (N / horizon) * W # λ × W
rows.append({
"滞在時間分布": name,
"CV(T)": cv,
"W=mean(T)": W,
"L_sim": L_sim,
"lambda*W": lamW,
"相対誤差": abs(L_sim - lamW) / lamW,
})
res = pd.DataFrame(rows)
res["相対誤差"] = res["相対誤差"].map(lambda x: f"{x:.2e}")
print(f"lambda = {lam} 件/時, 観測内到着数 N = {N}")
print(res.to_string(index=False, float_format=lambda x: f"{x:.4f}"))
出力:
lambda = 3.0 件/時, 観測内到着数 N = 59937
滞在時間分布 CV(T) W=mean(T) L_sim lambda*W 相対誤差
lognormal 1.3231 2.0003 5.9944 5.9946 2.97e-05
gamma(k=2) 0.7068 2.0021 5.9987 5.9999 2.05e-04
deterministic 0.0000 2.0000 5.9934 5.9937 4.55e-05
出力の意味:3つの滞在時間分布は形がまったく違います——変動係数 CV は 1.32(対数正規、指数より暴れる)/0.71(ガンマ)/0.00(一定)。指数分布なら CV=1 ですから、どれも非指数です。それでも時間平均から直接測った は、(ここでは )といずれも小数第3位まで一致しました。相対誤差は最大でも (0.02 %)程度で、これは観測窓の端で「まだ系内にいる数件」を取りこぼす有限長効果にすぎず、 で 0 に向かいます。分布の形を問わず (= )が成り立つことが、机上ではなくシミュレーションで確認できました。
⚠️ よくある誤解
- 「スループットは1工程を速めれば上がる」ではない:律速はボトルネック資源か需要で決まります。非ボトルネックを速めても全体の は上がりません(次回 ボトルネックとキャパシティ)。
- 「フロータイムを縮めると在庫は変わらない」ではない: が一定なら より、 を縮めたぶん仕掛在庫 がそのまま減少します。リードタイム短縮が在庫削減に直結する数理的理由です。
- 「リトルの法則は特定の分布が前提」ではない:第2節の面積論の通り分布によらない恒等式です。ただし定常(長期で入=出が釣り合う)が前提で、在庫が一方的に積み上がる過渡状態には単純には使えません。
関連ノート
- オペレーションズ・マネジメントとは( の導入元・本稿の前提)
- ボトルネックとキャパシティ(次のトピック・ を決めるのは何か)
- 第3章 在庫管理( を在庫削減・リードタイムに応用)/第4章 待ち行列理論(滞在時間 を分布から求める)
- 確率過程(マルコフ連鎖・ポアソン過程)(ポアソン過程・待ち行列の確率的土台)
- オペレーションズ・マネジメント 全体目次