🎓 レベル:標準 | 重要度:A(必須)
📎 前提:ボトルネックとキャパシティ(ボトルネック=最小キャパシティの資源が律速) | 制約の限界価値:線形計画による生産計画(シャドープライス) | 次:リーン生産・JIT・かんばん
要点(BLUF)
- 制約理論(TOC, Theory of Constraints)はゴールドラットの考え方で、出発点は「システムの産出は、最も弱い1つの資源=制約(ボトルネック)が律速する」(ボトルネックとキャパシティ)。だから全体の改善は、制約を見つけてそこに集中することに尽きます。
- 測る物差しはスループット会計の3指標——スループット (システムが売上を通じてお金を生み出す率=売上 − 真の変動費〔資材費〕)・在庫 (売るために購入したものに縛られたお金)・業務費用 ( を に変える費用)。狙いは 最大・ と 削減で、利益は 。
- 製品ミックスは「単位利益が高い順」では決めません。決め手は 制約1分あたりスループット 。これは 線形計画による生産計画 のシャドープライス(制約の限界価値)と同じもので、本ノートでは「単位利益順」が赤字、「制約1分あたりT順」が黒字になり、後者が LP 最適と一致することを示します。
- 制約に集中する手順が集中の5ステップ(①特定 ②徹底活用 ③従属 ④強化 ⑤繰り返す)、ラインで制約を守る仕組みがドラム-バッファ-ロープ。非制約をいくら速くしても産出は増えず、制約の手前のバッファが枯渇(スターベーション)を防ぐことをシミュレーションで確かめます。
1. スループット会計:T・I・OE で測る
会計の常識では、製品ごとに材料費だけでなく労務費や間接費(工場の電気代・管理者の給料・減価償却)を「配賦」して原価を出し、売価との差を製品利益と呼びます。TOC はこの原価計算(コストワールド)を意思決定の物差しにすることを拒否します。理由は、間接費の多くは生産量を1個増減しても変わらない(その期の固定費)のに、配賦は「1個あたり○円」とあたかも変動費のように見せかけ、判断を誤らせるからです。
代わりに使うのが**スループット会計(スループットワールド)**の3つの量です。
| 指標 | 読み | 定義 |
|---|---|---|
| スループット | 販売を通じてお金を生む率 = 売上 − 真に変動する費用(資材費・外注費)。労務費は含めない | |
| 在庫(投資) | 売るために購入したものに縛られたお金(原材料・仕掛・製品の購入価額) | |
| 業務費用 | を に変えるために使うお金(労務費・経費など、ほぼ期間固定) |
ここで肝心なのは の定義です。1個売ったときのスループットは「価格 − その1個に直接かかった資材費」だけで、人件費は引きません。なぜなら工員の給料は1個多く作っても変わらない( の一部)から。利益とキャッシュは次の関係になります。
は製品 の価格、 は資材費、 は販売量。 が期間固定なら、利益を最大化することは総スループット を最大化することと同じです。この単純な置き換えが、次節の「制約1分あたりT」という意思決定基準を生みます。
2. 集中の5ステップとドラム-バッファ-ロープ
制約は1つ(または少数)なので、改善努力をそこに集中します。TOC はこれを5つの集中ステップ(five focusing steps)として手順化しました。
flowchart LR S1["1. 制約を特定する"] --> S2["2. 制約を徹底活用する<br/>(1秒も遊ばせない)"] S2 --> S3["3. 他をすべて従属させる<br/>(非制約を制約の歩調に合わせる)"] S3 --> S4["4. 制約を強化する<br/>(能力を足す・外注する)"] S4 --> S5["5. 制約が動いたら繰り返す<br/>(惰性に戻らない)"] S5 -. "新しい制約へ" .-> S1
- ②徹底活用(exploit)が先で④強化(elevate)が後なのがポイント。お金をかけて能力を買い増す前に、まず今ある制約を遊ばせない(昼休みも止めない・段取りを制約の外でやる・不良を制約に流さない)。タダでできる改善を先に絞り切ります。
- **③従属(subordinate)**は「非制約は制約に合わせて働け」。非制約が自分の効率を上げようとフル稼働すると、制約の前に在庫が山積みになるだけ(ボトルネックとキャパシティ の「非ボトルネックの遊びは無駄ではない」)。
この③④をラインで実装する仕組みが**ドラム-バッファ-ロープ(DBR)**です。
flowchart LR REL["資材投入"] --> NB1["非制約工程<br/>(速い・余力あり)"] NB1 --> BUF["バッファ<br/>(制約の手前に在庫を置く)"] BUF --> DRUM["制約=ドラム<br/>(最も遅い・常に稼働させる)"] DRUM --> NB2["非制約工程<br/>(速い・余力あり)"] NB2 --> OUT["出荷"] DRUM -. "ロープ:ドラムの歩調でだけ投入する" .-> REL
- ドラム(drum)=制約。システム全体の歩調(太鼓)を刻むのは制約の処理ペースです。
- バッファ(buffer)=制約の手前に置く在庫(時間バッファ)。上流がばらついても制約が枯渇(スターベーション)しないための保険。制約が1分止まると、その1分はシステム全体が1分止まったのと同じ(取り返せない)。
- ロープ(rope)=制約の処理ペースに資材投入を縛る信号。投入を制約より速くしても、仕掛在庫()が膨らむだけで産出は増えません。ロープは投入を制約に同期させ、 を抑えます——これは次章 リーン生産・JIT・かんばん のプル(WIP を上限で縛る)と同じ発想です。
3. 数式:制約1分あたりスループットで順位付け
制約の能力(たとえばボトルネック工程の1週間ぶんの分)は有限です。複数製品がこの同じ希少資源を奪い合うとき、どの製品を優先すべきか。素朴な答えは「1個あたりの利益()が大きい製品から」ですが、これは間違いです。
正しい基準は、希少な制約時間を1分使ったときに得られるスループットで順位を付けること。
は製品 が制約で消費する時間(分/個)。なぜこれが正しいか。制約の総時間 (分)を配分する問題は
という線形計画(線形計画による生産計画)で、制約が1本だけのときの最適解は「単位資源あたりの価値が高い順に、需要上限 まで詰める」という貪欲法(分数ナップサック)で厳密に与えられます。そして最適でのボトルネックのシャドープライス(制約を1分広げたときの利益増)は、ちょうど最後に部分生産された「限界製品」の制約1分あたりTに等しくなります。つまり「制約1分あたりT」は TOC の優先順位基準であると同時に、LP の双対価格(制約の限界価値)そのものなのです。次のコードでこれを数値で確かめます。
4. 単位利益順 vs 制約1分あたりT順(コード)
製品A・B・Cが1つのボトルネック(週2400分)を奪い合います。**A は単位利益が最大(60円/個)だが制約を重く食う(30分/個)**ので制約1分あたりは2.0円と最低。逆に **C は単位利益が最小(30円)だが軽い(6分)**ので制約1分あたり5.0円と最高。「単位利益順」と「制約1分あたりT順」で生産を割り当て、総スループットと純利益( を引く)を比べます。最後に scipy.optimize.linprog で真の最適と突き合わせます。
import numpy as np
import pandas as pd
from scipy.optimize import linprog
# 3製品・1つのボトルネック資源(週2400分)。各製品の
# 価格・資材費 -> スループット T = 価格 - 資材費(円/個)
# ボトルネックでの所要時間(分/個)・週次需要上限(個)
prod = pd.DataFrame({
"製品": ["A", "B", "C"],
"価格": [120, 95, 70],
"資材費": [60, 50, 40],
"制約時間_分": [30, 15, 6],
"需要上限": [100, 100, 100],
})
prod["T_単位"] = prod["価格"] - prod["資材費"] # 単位スループット(円/個)
prod["T_制約1分"] = prod["T_単位"] / prod["制約時間_分"] # 制約1分あたりスループット
CAP = 2400.0 # ボトルネック能力(分/週)
OE = 5000.0 # 業務費用 OE(円/週・固定)
def allocate(df, order_col):
"""order_col の降順に貪欲にボトルネック時間を割り当て、生産量と総Tを返す。"""
d = df.sort_values(order_col, ascending=False).copy()
remain = CAP
qty = []
for _, r in d.iterrows():
take = min(r["需要上限"], remain / r["制約時間_分"]) # 残り時間で作れる上限
qty.append(take)
remain -= take * r["制約時間_分"]
d["生産量"] = qty
d["獲得T"] = d["生産量"] * d["T_単位"]
return d, d["獲得T"].sum()
by_profit, T_profit = allocate(prod, "T_単位") # 単位利益が高い順に作る
by_ratio, T_ratio = allocate(prod, "T_制約1分") # 制約1分あたりTが高い順に作る
print(prod[["製品", "T_単位", "制約時間_分", "T_制約1分"]].to_string(
index=False, float_format=lambda x: f"{x:.2f}"))
print()
print("--- 方針1:単位利益(T_単位)が高い順に生産 ---")
print(by_profit[["製品", "生産量", "獲得T"]].to_string(
index=False, float_format=lambda x: f"{x:.1f}"))
print(f"総スループット T = {T_profit:.0f} 円/週, 純利益 T-OE = {T_profit-OE:.0f} 円/週")
print()
print("--- 方針2:制約1分あたりT(T_制約1分)が高い順に生産 ---")
print(by_ratio[["製品", "生産量", "獲得T"]].to_string(
index=False, float_format=lambda x: f"{x:.1f}"))
print(f"総スループット T = {T_ratio:.0f} 円/週, 純利益 T-OE = {T_ratio-OE:.0f} 円/週")
# LP(05-01 と同じ linprog)で真の最適を確認:max sum(T_i*x_i) s.t. sum(時間_i*x_i)<=CAP, 0<=x<=需要
c = -prod["T_単位"].to_numpy(dtype=float)
A_ub = prod["制約時間_分"].to_numpy(dtype=float).reshape(1, -1)
res = linprog(c=c, A_ub=A_ub, b_ub=[CAP],
bounds=[(0, u) for u in prod["需要上限"]], method="highs")
print()
print(f"LP 最適スループット = {-res.fun:.0f} 円/週(制約1分あたりT順と一致)")
print(f"ボトルネックのシャドープライス = {-res.ineqlin.marginals[0]:.2f} 円/分")
print(f" -> 限界製品A の T_制約1分 = {prod['T_制約1分'].min():.2f} 円/分 に一致")
出力:
製品 T_単位 制約時間_分 T_制約1分
A 60 30 2.00
B 45 15 3.00
C 30 6 5.00
--- 方針1:単位利益(T_単位)が高い順に生産 ---
製品 生産量 獲得T
A 80.0 4800.0
B 0.0 0.0
C 0.0 0.0
総スループット T = 4800 円/週, 純利益 T-OE = -200 円/週
--- 方針2:制約1分あたりT(T_制約1分)が高い順に生産 ---
製品 生産量 獲得T
C 100.0 3000.0
B 100.0 4500.0
A 10.0 600.0
総スループット T = 8100 円/週, 純利益 T-OE = 3100 円/週
LP 最適スループット = 8100 円/週(制約1分あたりT順と一致)
ボトルネックのシャドープライス = 2.00 円/分
-> 限界製品A の T_制約1分 = 2.00 円/分 に一致
出力の意味:A は単位利益が最大(60円)なので、「儲かる製品から作れ」という直感に従うと A だけを80個作って制約を使い切り、総スループット4800円・ を引くと純利益 −200円の赤字です。ところが制約1分あたりTは A が最低(2.0円/分)。軽くて回転の速い C → B → A の順に詰めると、C を100個・B を100個・A を10個作れて総スループット8100円・純利益3100円の黒字。同じ制約・同じ需要なのに、順位の付け方だけで赤字と黒字が入れ替わりました。そして「制約1分あたりT順」の8100円は、linprog が出したLP 最適スループット8100円とぴたり一致。さらにボトルネックのシャドープライスは2.00円/分で、これは最後に部分生産された限界製品 A の制約1分あたりT(2.0円/分)と一致します。「制約1分あたりT」は TOC の優先順位であり、同時に 線形計画による生産計画 で見た制約の限界価値(双対価格)そのものだと数値で確認できました。
受注可否もこの物差しで決まります。新規の特注が来たとき、(a) その注文が制約を使わないなら、スループット 価格 − 資材費 でありさえすれば受けるべき( は固定なので が丸ごと利益に乗る。原価計算が「配賦原価割れ」と言って断るのは誤り)。(b) その注文が制約を使うなら、注文の制約1分あたりT が、押しのける限界製品の値(ここでは A の2.0円/分=シャドープライス)を上回るときだけ受ける。たとえば制約を20分使い 円の特注は 円/分 なので、A を減らしてでも受けるべき。逆に同じ20分で 円(円/分 )なら断る——シャドープライスが受注のハードルレートになります。
5. 非制約を速くしても無駄・バッファが制約を守る(コード)
ボトルネックとキャパシティ で「非ボトルネックを速くしても産出は増えない」と学びました。TOC の言葉では「局所効率を上げても、制約でないところを強化(elevate)しても全体スループットは1ミリも増えない」。さらに DBR のバッファが、上流のばらつきから制約を守る効果も確かめます。段1(供給・速い )→ バッファ(容量 )→ 段2(ドラム=制約・遅い )の2段ラインを離散事象シミュレーションで回し、ブロッキング(バッファ満杯で段1が止まる)とスターベーション(バッファ空で段2が止まる)を陽に扱います。
import numpy as np
def sim_line(mu1, mu2, K, n, seed):
"""飽和入力の2段直列ライン。段1=供給(速い), 段2=ドラム/制約(遅い),
その間のバッファ容量 K。ブロッキング(段1)とスターベーション(段2)を陽に扱い、
段2の完成からスループットを測る。"""
rng = np.random.default_rng(seed)
S1 = rng.exponential(1.0 / mu1, n) # 段1のサービス時間
S2 = rng.exponential(1.0 / mu2, n) # 段2のサービス時間
s2 = np.zeros(n) # 段2が部品 i の加工を始める時刻
c2 = np.zeros(n) # 段2が部品 i を完成(退去)させる時刻
prev_e1 = 0.0 # 段1が直前の部品を払い出した時刻 e1[i-1]
for i in range(n):
c1 = prev_e1 + S1[i] # 段1が部品 i を作り終える
prev_c2 = c2[i - 1] if i >= 1 else 0.0 # 段2が直前を終えた時刻
if K == 0: # バッファ無し=直送(段1はブロック)
ei = max(c1, prev_c2)
si = ei
else: # バッファ K:i-K を段2が始めれば空く
room = s2[i - K] if i - K >= 0 else 0.0
ei = max(c1, room)
si = max(prev_c2, ei)
s2[i] = si
c2[i] = si + S2[i]
prev_e1 = ei
warm = n // 10 # 立ち上がりを捨てる
thru = (n - warm) / (c2[-1] - c2[warm - 1]) # スループット(個/時)
return thru
mu1, mu2 = 1.2, 1.0 # 段1(非ボトルネック・速い), 段2(ドラム=制約・遅い)
n = 200_000
print(f"段1(供給)率 mu1={mu1}, 段2(ドラム=制約)率 mu2={mu2}")
print("無限バッファなら スループット = 制約 mu2 = 1.000(段2が律速)")
print()
print("バッファK スループット 制約能力に対する達成率")
for K in [0, 1, 2, 5, 10, 30]:
thru = sim_line(mu1, mu2, K, n, seed=42)
print(f"{K:6d} {thru:11.4f} {thru/mu2*100:8.1f}%")
print()
print("=== 集中の第4ステップ「制約を強化」:どこを速くすべきか(K=30 で比較)===")
base = sim_line(1.2, 1.0, 30, n, seed=42)
nonbn = sim_line(2.0, 1.0, 30, n, seed=42) # 非ボトルネック(段1)を 1.2->2.0 に強化
bn = sim_line(1.2, 1.15, 30, n, seed=42) # ボトルネック(段2)を 1.0->1.15 に強化
print(f"現状 (mu1=1.2, mu2=1.00): スループット {base:.4f}")
print(f"非制約を強化 (mu1=2.0, mu2=1.00): スループット {nonbn:.4f} (ほぼ不変)")
print(f"制約を強化 (mu1=1.2, mu2=1.15): スループット {bn:.4f} (上がる)")
出力:
段1(供給)率 mu1=1.2, 段2(ドラム=制約)率 mu2=1.0
無限バッファなら スループット = 制約 mu2 = 1.000(段2が律速)
バッファK スループット 制約能力に対する達成率
0 0.7265 72.6%
1 0.8152 81.5%
2 0.8674 86.7%
5 0.9408 94.1%
10 0.9815 98.1%
30 1.0018 100.2%
=== 集中の第4ステップ「制約を強化」:どこを速くすべきか(K=30 で比較)===
現状 (mu1=1.2, mu2=1.00): スループット 1.0018
非制約を強化 (mu1=2.0, mu2=1.00): スループット 1.0024 (ほぼ不変)
制約を強化 (mu1=1.2, mu2=1.15): スループット 1.1358 (上がる)
出力の意味:まずバッファの効果。制約(段2・能力1.0)の手前にバッファが無い(K=0)と、スループットは0.7265=制約能力の72.6%しか出ません。段1がたまたま遅れると段2が即スターベーション(手待ち)になり、止まった時間は二度と取り戻せないからです。バッファを と増やすと81.5% → 86.7% → 94.1% → 98.1% と回復し、 でほぼ100%(1.0018、0.2%の超過は有限長シミュの誤差で、漸近的な上限は制約能力1.000)。制約の手前のわずかな在庫が、ばらつきによる枯渇を吸収して産出を守る——これが DBR のバッファです。次にどこを強化すべきか。バッファ十分()の状態から、非制約の段1を と大幅に速くしても、スループットは とほぼ不変。律速は段2のままだからです。一方制約の段2を (15%)速くすると、スループットは へ素直に上がります。改善のリターンは制約に投資したときだけ得られる。「全工程をまんべんなく効率化」は、制約以外への投資ぶんがそっくり無駄になるのです。
⚠️ よくある誤解
- 「局所効率を上げれば全体も良くなる」ではない:非制約の稼働率を上げても、産出は制約で頭打ち(ボトルネックとキャパシティ)。非制約をフル稼働させると、制約の前に仕掛在庫()が積み上がるだけで は増えず、 と (保管・管理)はむしろ悪化します。非制約は遊ばせてよい——その遊び(余力)はバッファを満たし制約を守るために使うのが正解です。
- 「スループット会計=原価計算」ではない:原価計算は間接費を1個あたりに配賦して「製品原価」を作りますが、間接費の多くは生産量で変わらない期間費用()です。これを変動費のように扱うと、本ノートのように単位利益(≒配賦後の見かけの儲け)で並べて赤字を招きます。TOC は 売上 − 真の変動費(資材費)だけを製品に紐づけ、 はシステム全体で一括して引きます。配賦をやめると判断が変わるのが要点です。
- 「制約を強化したら終わり」ではない:制約を と強化すると、いずれ別の資源が新しい制約になり(ボトルネックとキャパシティ の「ボトルネックは移動する」、線形計画による生産計画 の「シャドープライスは局所的」)、制約1分あたりTの順位もシャドープライスも変わります。第5ステップ「繰り返す」は惰性に戻るなという警告——昨日のボトルネック対策を、制約が動いた後も後生大事に続けてはいけません。
- 「制約は機械(物理)だけ」ではない:制約は方針(過度なバッチ規制・部門別効率指標)や市場(需要そのものが制約)にもあります。物理制約を解いていくと、最後に残る制約はしばしば組織の方針です(要最新確認)。本ノートは数理が効く物理制約に絞りましたが、TOC の射程はそこに留まりません。
関連ノート
- ボトルネックとキャパシティ(制約=最小キャパシティの資源・律速と余力・ボトルネックは移動する。TOC の物理的土台)
- 線形計画による生産計画(シャドープライス=制約の限界価値。本ノートの「制約1分あたりT」は1制約LPの双対価格そのもの)
- リーン生産・JIT・かんばん(次のトピック・ロープ=プルでWIPを縛る発想の一般化。かんばんはDBRのロープに対応)
- ばらつきと改善の数理(なぜバッファが要るのか=ばらつきが制約を枯渇させる。キングマンの式で定量化)
- オペレーションズ・マネジメント 全体目次