- マネジメント
クリティカルパスの求め方を例題3問で解説|最早・最遅と余裕日数の計算
クリティカルパスとは、開始から終了までの経路のうち所要日数の合計が最も長い経路のことで、その長さがプロジェクトの最短の所要日数になります。 この経路上の作業は余裕日数が0で、1日遅れると全体の終了日も1日遅れます。
求め方は、経路をすべて書き出して合計を比べる方法と、前進計算で最早の日付を、後退計算で最遅の日付を求めて余裕日数が0の作業をつなぐ方法の2つがあります。作業が増えると後者のほうが速く確実です。この記事では、難易度の違う例題3問を計算表で1行ずつ解き、同じ問題をPythonで検算します。アローダイアグラムはMermaidとPlantUMLのコードで再現できます。
確認環境:Ubuntu 24.04 / Python 3.11.15(計算コードを実行)、Mermaid 11.14.0(@mermaid-js/mermaid-cli と Chromium で描画)、PlantUML 1.2020.02 + Graphviz 2.43.0(OpenJDK 21 で描画)。
クリティカルパス計算の早見表
| 求めるもの | 略号 | 計算式 | 計算の向き |
|---|---|---|---|
| 最早開始 | ES | 先行作業のEFのうち最大(先行がなければ0) | 前から後ろへ |
| 最早終了 | EF | ES + 所要日数 | 前から後ろへ |
| 最遅終了 | LF | 後続作業のLSのうち最小(後続がなければ全体の所要日数) | 後ろから前へ |
| 最遅開始 | LS | LF − 所要日数 | 後ろから前へ |
| トータルフロート | TF | LS − ES(= LF − EF) | 両方が出てから |
| フリーフロート | FF | 後続作業のESのうち最小 − EF | 両方が出てから |
| クリティカルパス | CP | TFが0の作業を開始から終了までつないだ経路 | 最後 |
合流点では、前進計算は「大きいほう」、後退計算は「小さいほう」を採る。計算でつまずくのは、ほぼこの1点です。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「クリティカルパスの求め方を例題3問で解説|最早・最遅と余裕日数の計算」
用語の早見表
| 用語 | 別の呼び方 | 意味 |
|---|---|---|
| アローダイアグラム | PERT図、ネットワーク図 | 作業を矢印(矢線)、作業の区切りを丸(結合点)で表した図 |
| 結合点 | ノード、イベント | 作業の開始・終了を表す丸。番号を付ける |
| 先行作業 | 前作業 | その作業を始める前に終わっている必要がある作業 |
| 後続作業 | 後作業 | その作業が終わってから始められる作業 |
| 前進計算 | フォワードパス | 開始から順に、最も早い開始・終了を求める計算 |
| 後退計算 | バックワードパス | 終了からさかのぼり、最も遅い開始・終了を求める計算 |
| トータルフロート | 全余裕、余裕日数 | 全体の終了日を動かさずに遅らせられる日数 |
| フリーフロート | 自由余裕 | 後続作業の最早開始を動かさずに遅らせられる日数 |
| ダミー作業 | ダミー | 所要日数0の、順序関係だけを表す点線の矢印 |
| 最早結合点時刻 | 最早時刻 | その結合点に最も早く到達できる時点 |
| 最遅結合点時刻 | 最遅時刻 | 全体を遅らせないために、その結合点に到達していなければならない時点 |
| 三点見積もり | PERT見積もり | 楽観値・最可能値・悲観値から期待値を求める方法 |
この記事では開始時点を0日目として数えます(ESが0、所要3日ならEFは3)。開始を1日目と数える流儀では ES と LS が1ずつ大きくなり、EF=ES+日数−1 になりますが、余裕日数とクリティカルパスは同じ結果になります。
求め方の手順(5ステップ)
- 作業・所要日数・先行作業を表にする
- 前進計算:先頭の作業から順に ES と EF を埋める。最後の作業の EF の最大値が全体の所要日数
- 後退計算:最後の作業から順に LF と LS を埋める
- TF=LS−ES、FF=後続のESの最小−EF を計算する
- TFが0の作業をつないだ経路がクリティカルパス
例題1(やさしい):5作業、合流が1か所
| 作業 | 所要日数 | 先行作業 |
|---|---|---|
| A | 3 | なし |
| B | 4 | A |
| C | 2 | A |
| D | 5 | B, C |
| E | 2 | D |
前進計算
- A:先行がないので ES=0、EF=0+3=3
- B:ES=AのEF=3、EF=3+4=7
- C:ES=3、EF=3+2=5
- D:先行はBとC。EFの大きいほうを採って ES=max(7, 5)=7、EF=7+5=12
- E:ES=12、EF=12+2=14 → 全体の所要日数は14日
後退計算
- E:後続がないので LF=14、LS=14−2=12
- D:LF=EのLS=12、LS=12−5=7
- B:LF=DのLS=7、LS=7−4=3
- C:LF=7、LS=7−2=5
- A:後続はBとC。LSの小さいほうを採って LF=min(3, 5)=3、LS=3−3=0
計算表
| 作業 | 日数 | ES | EF | LS | LF | TF | FF | クリティカル |
|---|---|---|---|---|---|---|---|---|
| A | 3 | 0 | 3 | 0 | 3 | 0 | 0 | ○ |
| B | 4 | 3 | 7 | 3 | 7 | 0 | 0 | ○ |
| C | 2 | 3 | 5 | 5 | 7 | 2 | 2 | |
| D | 5 | 7 | 12 | 7 | 12 | 0 | 0 | ○ |
| E | 2 | 12 | 14 | 12 | 14 | 0 | 0 | ○ |
クリティカルパスは A→B→D→E、14日です。Cは2日までなら遅れても全体に響きません。
アローダイアグラム(Mermaid)
太い線がクリティカルパスです。BとCはどちらも結合点2から始まり、Dの前で合流します。同じ2つの結合点を2本の矢印で結ぶと作業を区別できなくなるため、Cの終点に結合点3を置き、点線のダミー作業で4へつなぎます。
flowchart LR
n1((1)) == "A 3日" ==> n2((2))
n2 == "B 4日" ==> n4((4))
n2 -- "C 2日" --> n3((3))
n3 -. "ダミー 0日" .-> n4
n4 == "D 5日" ==> n5((5))
n5 == "E 2日" ==> n6((6))
例題2(ふつう):8作業、経路が3本
| 作業 | 内容の例 | 所要日数 | 先行作業 |
|---|---|---|---|
| A | 要件の整理 | 5 | なし |
| B | 画面の設計 | 4 | A |
| C | データベースの設計 | 6 | A |
| D | 画面の開発 | 3 | B |
| E | APIの開発 | 5 | C |
| F | 移行データの準備 | 4 | C |
| G | 結合テスト | 2 | D, E |
| H | 受け入れ確認 | 3 | F, G |
計算表
| 作業 | 日数 | ES | EF | LS | LF | TF | FF | クリティカル |
|---|---|---|---|---|---|---|---|---|
| A | 5 | 0 | 5 | 0 | 5 | 0 | 0 | ○ |
| B | 4 | 5 | 9 | 9 | 13 | 4 | 0 | |
| C | 6 | 5 | 11 | 5 | 11 | 0 | 0 | ○ |
| D | 3 | 9 | 12 | 13 | 16 | 4 | 4 | |
| E | 5 | 11 | 16 | 11 | 16 | 0 | 0 | ○ |
| F | 4 | 11 | 15 | 14 | 18 | 3 | 3 | |
| G | 2 | 16 | 18 | 16 | 18 | 0 | 0 | ○ |
| H | 3 | 18 | 21 | 18 | 21 | 0 | 0 | ○ |
合流点の計算はこうなります。
- GのES=max(DのEF 12, EのEF 16)=16
- HのES=max(FのEF 15, GのEF 18)=18
- CのLF=min(EのLS 11, FのLS 14)=11
- AのLF=min(BのLS 9, CのLS 5)=5
クリティカルパスは A→C→E→G→H、21日です。
トータルフロートとフリーフロートの違いが出る行
BとDはどちらもTFが4ですが、FFはBが0、Dが4です。
- BのFF=DのES 9 − BのEF 9=0。Bが1日遅れると、後続のDの開始も1日遅れます
- DのFF=GのES 16 − DのEF 12=4。Dは4日まで遅れてもGの開始に響きません
B→Dの4日の余裕は、この経路で共有している余裕です。Bで2日使えば、Dに残るのは2日です。TFだけを見て「BもDも4日ずつ遅れてよい」と読むと、合計8日遅れてクリティカルパスを超えてしまいます。
アローダイアグラム(Mermaid)
flowchart LR
n1((1)) == "A 5日" ==> n2((2))
n2 -- "B 4日" --> n3((3))
n2 == "C 6日" ==> n4((4))
n3 -- "D 3日" --> n5((5))
n4 == "E 5日" ==> n5
n4 -- "F 4日" --> n6((6))
n5 == "G 2日" ==> n6
n6 == "H 3日" ==> n7((7))
例題3(むずかしい):10作業、ダミー作業が3本
| 作業 | 所要日数 | 先行作業 |
|---|---|---|
| A | 3 | なし |
| B | 5 | なし |
| C | 4 | A |
| D | 6 | A, B |
| E | 3 | C |
| F | 5 | C, D |
| G | 4 | D |
| H | 2 | E |
| I | 3 | F, G |
| J | 3 | H, I |
開始できる作業が2つあり、先行作業の組み合わせが作業ごとに違います。
計算表
| 作業 | 日数 | ES | EF | LS | LF | TF | FF | クリティカル |
|---|---|---|---|---|---|---|---|---|
| A | 3 | 0 | 3 | 2 | 5 | 2 | 0 | |
| B | 5 | 0 | 5 | 0 | 5 | 0 | 0 | ○ |
| C | 4 | 3 | 7 | 7 | 11 | 4 | 0 | |
| D | 6 | 5 | 11 | 5 | 11 | 0 | 0 | ○ |
| E | 3 | 7 | 10 | 14 | 17 | 7 | 0 | |
| F | 5 | 11 | 16 | 11 | 16 | 0 | 0 | ○ |
| G | 4 | 11 | 15 | 12 | 16 | 1 | 1 | |
| H | 2 | 10 | 12 | 17 | 19 | 7 | 7 | |
| I | 3 | 16 | 19 | 16 | 19 | 0 | 0 | ○ |
| J | 3 | 19 | 22 | 19 | 22 | 0 | 0 | ○ |
要所の計算です。
- DのES=max(AのEF 3, BのEF 5)=5
- FのES=max(CのEF 7, DのEF 11)=11
- AのLF=min(CのLS 7, DのLS 5)=5 → LS=2、TF=2
- CのLF=min(EのLS 14, FのLS 11)=11 → TF=4
- DのLF=min(FのLS 11, GのLS 12)=11 → TF=0
クリティカルパスは B→D→F→I→J、22日です。AはTFが2ありますが、FFは0(CのES 3 − AのEF 3)なので、Aが遅れるとCの開始はすぐに遅れます。
ダミー作業が必要になる理由
アローダイアグラムでは、1つの結合点から出る作業は「その結合点に入るすべての作業」を先行作業に持ちます。例題3では次の関係があるため、結合点を共有すると余計な順序が生まれます。
| 関係 | 結合点を共有すると | ダミーでの解決 |
|---|---|---|
| Cの先行はAだけ、Dの先行はAとB | CがBの終了も待つことになる | Aの終点(2)からBの終点(3)へダミー1 |
| Eの先行はCだけ、Fの先行はCとD | EがDの終了も待つことになる | Cの終点(4)からFの始点(6)へダミー2 |
| Gの先行はDだけ、Fの先行はCとD | GがCの終了も待つことになる | Dの終点(5)からFの始点(6)へダミー3 |
ダミー作業は所要日数0の作業として、ほかの矢印と同じように計算に入れます。ダミー3はクリティカルパスの途中(D→F)に入っています。
アローダイアグラム(Mermaid)
flowchart LR
n1((1)) -- "A 3日" --> n2((2))
n1 == "B 5日" ==> n3((3))
n2 -. "ダミー1" .-> n3
n2 -- "C 4日" --> n4((4))
n3 == "D 6日" ==> n5((5))
n4 -. "ダミー2" .-> n6((6))
n5 -. "ダミー3" .-> n6
n4 -- "E 3日" --> n8((8))
n6 == "F 5日" ==> n7((7))
n5 -- "G 4日" --> n7
n8 -- "H 2日" --> n9((9))
n7 == "I 3日" ==> n9
n9 == "J 3日" ==> n10((10))
アローダイアグラム(PlantUML)
PlantUMLでは、丸をユースケースの記法 (1) で書くと結合点らしく描けます。点線は ..>、色と太さは矢印の中の [#red,bold] で指定します。
@startuml
left to right direction
skinparam usecase {
BackgroundColor White
BorderColor Black
}
(1) --> (2) : A 3日
(1) -[#red,bold]-> (3) : B 5日
(2) ..> (3) : ダミー1
(2) --> (4) : C 4日
(3) -[#red,bold]-> (5) : D 6日
(4) ..> (6) : ダミー2
(5) .[#red].> (6) : ダミー3
(4) --> (8) : E 3日
(6) -[#red,bold]-> (7) : F 5日
(5) --> (7) : G 4日
(8) --> (9) : H 2日
(7) -[#red,bold]-> (9) : I 3日
(9) -[#red,bold]-> (10) : J 3日
@enduml
left to right direction を付けると左から右へ流れる図になります。描画は java -jar plantuml.jar q3.puml で行い、同じフォルダにPNGができます。この種類の図の配置にはGraphvizを使います。
結合点の時刻で検算する
ダミーを含む矢印の一覧から、結合点ごとの最早時刻・最遅時刻を求めるコードです。作業の表から計算した結果と一致するかを確かめられます。
"""アローダイアグラム(結合点と矢線)の最早結合点時刻・最遅結合点時刻。"""
# (始点, 終点, 作業名, 日数) ダミー作業は日数0
EDGES = [
(1, 2, "A", 3), (1, 3, "B", 5), (2, 3, "ダミー1", 0), (2, 4, "C", 4),
(3, 5, "D", 6), (4, 6, "ダミー2", 0), (5, 6, "ダミー3", 0), (4, 8, "E", 3),
(6, 7, "F", 5), (5, 7, "G", 4), (8, 9, "H", 2), (7, 9, "I", 3), (9, 10, "J", 3),
]
nodes = sorted({n for e in EDGES for n in e[:2]})
early = {n: 0 for n in nodes}
for _ in nodes: # 番号順でなくても収束するよう、結合点の数だけ繰り返す
for i, j, _name, d in EDGES:
early[j] = max(early[j], early[i] + d)
end = max(early.values())
late = {n: end for n in nodes}
for _ in nodes:
for i, j, _name, d in EDGES:
late[i] = min(late[i], late[j] - d)
print("結合点 最早 最遅 余裕")
for n in nodes:
print(f"{n:>4} {early[n]:>5} {late[n]:>4} {late[n] - early[n]:>4}")
print()
print("矢線 日数 TF FF")
for i, j, name, d in EDGES:
tf = late[j] - early[i] - d
ff = early[j] - early[i] - d
mark = " *" if tf == 0 else ""
print(f"{i}→{j:<3} {name:<5} {d:>2} {tf:>3} {ff:>3}{mark}")
実行結果:
結合点 最早 最遅 余裕
1 0 0 0
2 3 5 2
3 5 5 0
4 7 11 4
5 11 11 0
6 11 11 0
7 16 16 0
8 10 17 7
9 19 19 0
10 22 22 0
矢線 日数 TF FF
1→2 A 3 2 0
1→3 B 5 0 0 *
2→3 ダミー1 0 2 2
2→4 C 4 4 0
3→5 D 6 0 0 *
4→6 ダミー2 0 4 4
5→6 ダミー3 0 0 0 *
4→8 E 3 7 0
6→7 F 5 0 0 *
5→7 G 4 1 1
8→9 H 2 7 7
7→9 I 3 0 0 *
9→10 J 3 0 0 *
最早と最遅が等しい結合点(余裕0)は 1・3・5・6・7・9・10 で、これらを結ぶTFが0の矢印が B→D→ダミー3→F→I→J です。作業の表から求めた結果と同じになりました。
Pythonで前進計算・後退計算を検算する
3問をまとめて解くコードです。標準ライブラリだけで動きます。cpm.py として保存して実行します。
"""クリティカルパス法(CPM)の前進計算・後退計算。標準ライブラリのみ。"""
# 作業名: (所要日数, [先行作業])
Q1 = {
"A": (3, []),
"B": (4, ["A"]),
"C": (2, ["A"]),
"D": (5, ["B", "C"]),
"E": (2, ["D"]),
}
Q2 = {
"A": (5, []),
"B": (4, ["A"]),
"C": (6, ["A"]),
"D": (3, ["B"]),
"E": (5, ["C"]),
"F": (4, ["C"]),
"G": (2, ["D", "E"]),
"H": (3, ["F", "G"]),
}
Q3 = {
"A": (3, []),
"B": (5, []),
"C": (4, ["A"]),
"D": (6, ["A", "B"]),
"E": (3, ["C"]),
"F": (5, ["C", "D"]),
"G": (4, ["D"]),
"H": (2, ["E"]),
"I": (3, ["F", "G"]),
"J": (3, ["H", "I"]),
}
def topo_order(acts):
"""先行作業が必ず先に来る順番に並べる。"""
order, done = [], set()
while len(order) < len(acts):
ready = [a for a in acts if a not in done and all(p in done for p in acts[a][1])]
if not ready:
raise ValueError("先行関係が循環しています")
for a in ready:
order.append(a)
done.add(a)
return order
def cpm(acts):
order = topo_order(acts)
succ = {a: [] for a in acts}
for a, (_, preds) in acts.items():
for p in preds:
succ[p].append(a)
# 前進計算:最早開始 ES = 先行作業の最早終了 EF の最大
es, ef = {}, {}
for a in order:
dur, preds = acts[a]
es[a] = max((ef[p] for p in preds), default=0)
ef[a] = es[a] + dur
total = max(ef.values())
# 後退計算:最遅終了 LF = 後続作業の最遅開始 LS の最小
ls, lf = {}, {}
for a in reversed(order):
lf[a] = min((ls[s] for s in succ[a]), default=total)
ls[a] = lf[a] - acts[a][0]
rows = []
for a in acts:
tf = ls[a] - es[a]
ff = min((es[s] for s in succ[a]), default=total) - ef[a]
rows.append((a, acts[a][0], ",".join(acts[a][1]) or "-", es[a], ef[a], ls[a], lf[a], tf, ff))
return total, rows, succ
def critical_paths(acts, rows, succ):
"""トータルフロート0の作業だけをたどった経路をすべて返す。"""
crit = {r[0] for r in rows if r[7] == 0}
ef = {r[0]: r[4] for r in rows}
es = {r[0]: r[3] for r in rows}
paths = []
def walk(a, path):
nxt = [s for s in succ[a] if s in crit and es[s] == ef[a]]
if not nxt:
paths.append(path)
for s in nxt:
walk(s, path + [s])
for a in acts:
if a in crit and es[a] == 0:
walk(a, [a])
return paths
def all_paths(acts, succ):
"""検算用:開始から終了までの全経路と合計日数。"""
result = []
def walk(a, path):
if not succ[a]:
result.append(("→".join(path), sum(acts[x][0] for x in path)))
for s in succ[a]:
walk(s, path + [s])
for a in acts:
if not acts[a][1]:
walk(a, [a])
return sorted(result, key=lambda x: -x[1])
def report(title, acts):
total, rows, succ = cpm(acts)
print(f"=== {title} ===")
print("作業 日数 先行 ES EF LS LF TF FF")
for a, d, p, es, ef, ls, lf, tf, ff in rows:
mark = " *" if tf == 0 else ""
print(f"{a:<4} {d:>3} {p:<5} {es:>3} {ef:>3} {ls:>3} {lf:>3} {tf:>3} {ff:>3}{mark}")
print(f"最短の所要日数: {total}日")
for p in critical_paths(acts, rows, succ):
print("クリティカルパス:", "→".join(p))
print("全経路:", "、".join(f"{p}={d}" for p, d in all_paths(acts, succ)))
print()
if __name__ == "__main__":
report("例題1", Q1)
report("例題2", Q2)
report("例題3", Q3)
python3 cpm.py
実行結果(* はトータルフロートが0の作業):
=== 例題1 ===
作業 日数 先行 ES EF LS LF TF FF
A 3 - 0 3 0 3 0 0 *
B 4 A 3 7 3 7 0 0 *
C 2 A 3 5 5 7 2 2
D 5 B,C 7 12 7 12 0 0 *
E 2 D 12 14 12 14 0 0 *
最短の所要日数: 14日
クリティカルパス: A→B→D→E
全経路: A→B→D→E=14、A→C→D→E=12
=== 例題2 ===
作業 日数 先行 ES EF LS LF TF FF
A 5 - 0 5 0 5 0 0 *
B 4 A 5 9 9 13 4 0
C 6 A 5 11 5 11 0 0 *
D 3 B 9 12 13 16 4 4
E 5 C 11 16 11 16 0 0 *
F 4 C 11 15 14 18 3 3
G 2 D,E 16 18 16 18 0 0 *
H 3 F,G 18 21 18 21 0 0 *
最短の所要日数: 21日
クリティカルパス: A→C→E→G→H
全経路: A→C→E→G→H=21、A→C→F→H=18、A→B→D→G→H=17
=== 例題3 ===
作業 日数 先行 ES EF LS LF TF FF
A 3 - 0 3 2 5 2 0
B 5 - 0 5 0 5 0 0 *
C 4 A 3 7 7 11 4 0
D 6 A,B 5 11 5 11 0 0 *
E 3 C 7 10 14 17 7 0
F 5 C,D 11 16 11 16 0 0 *
G 4 D 11 15 12 16 1 1
H 2 E 10 12 17 19 7 7
I 3 F,G 16 19 16 19 0 0 *
J 3 H,I 19 22 19 22 0 0 *
最短の所要日数: 22日
クリティカルパス: B→D→F→I→J
全経路: B→D→F→I→J=22、B→D→G→I→J=21、A→D→F→I→J=20、A→D→G→I→J=19、A→C→F→I→J=18、A→C→E→H→J=15
最後の「全経路」は、経路をすべて書き出して合計を比べる方法の結果です。最も長い経路と、TFが0の作業をつないだ経路が一致していることが分かります。また、各経路の合計とクリティカルパスの長さの差は、その経路で使える余裕の合計になります(例題2の A→B→D→G→H は 21−17=4日で、BとDのTFと同じ値)。
自分の問題を解くときは、辞書の形で "作業名": (所要日数, [先行作業]) を書き、report("題名", 辞書) を呼びます。先行関係が循環していると ValueError: 先行関係が循環しています で止まるので、入力の誤りにも気づけます。
三点見積もり(PERT)の期待値と分散
所要日数に幅があるときは、楽観値(o)・最可能値(m)・悲観値(p)の3つを見積もり、次の式で1つの値にまとめます。
| 求めるもの | 式 |
|---|---|
| 期待値 | (o + 4m + p) ÷ 6 |
| 分散 | ((p − o) ÷ 6) の2乗 |
| 標準偏差 | 分散の平方根 |
| 経路全体の期待値 | 経路上の各作業の期待値の合計 |
| 経路全体の分散 | 経路上の各作業の分散の合計(作業どうしが独立と考える場合) |
例題2のクリティカルパス(A→C→E→G→H)に、3つの見積もりを付けて計算します。
"""三点見積もり(PERT):期待値・分散と、期日までに終わる確率。"""
from math import sqrt
from statistics import NormalDist
# 例題2のクリティカルパス上の作業: (楽観値, 最可能値, 悲観値)
ESTIMATES = {
"A": (4, 5, 6),
"C": (4, 6, 8),
"E": (3, 5, 10),
"G": (1, 2, 3),
"H": (2, 3, 7),
}
print("作業 楽観 最可能 悲観 期待値 分散")
sum_te = sum_var = 0.0
for name, (o, m, p) in ESTIMATES.items():
te = (o + 4 * m + p) / 6
var = ((p - o) / 6) ** 2
sum_te += te
sum_var += var
print(f"{name:<4} {o:>3} {m:>5} {p:>5} {te:>6.2f} {var:>6.3f}")
sd = sqrt(sum_var)
print(f"合計の期待値: {sum_te:.2f}日 分散: {sum_var:.3f} 標準偏差: {sd:.2f}日")
dist = NormalDist(mu=sum_te, sigma=sd)
for due in (21, 22, 24, 25):
z = (due - sum_te) / sd
print(f"{due}日以内に終わる確率: {dist.cdf(due) * 100:5.1f}% (z = {z:+.2f})")
実行結果:
作業 楽観 最可能 悲観 期待値 分散
A 4 5 6 5.00 0.111
C 4 6 8 6.00 0.444
E 3 5 10 5.50 1.361
G 1 2 3 2.00 0.111
H 2 3 7 3.50 0.694
合計の期待値: 22.00日 分散: 2.722 標準偏差: 1.65日
21日以内に終わる確率: 27.2% (z = -0.61)
22日以内に終わる確率: 50.0% (z = +0.00)
24日以内に終わる確率: 88.7% (z = +1.21)
25日以内に終わる確率: 96.5% (z = +1.82)
読み方は次のとおりです。
- 最可能値だけを足すと 5+6+5+2+3=21日ですが、期待値の合計は22日です。EとHの悲観値が大きく、期待値が後ろへ寄るためです
- 分散が最も大きいのはE(1.361)。見積もりの幅が広い作業ほど、全体の日程のぶれに効きます
- 経路全体の日数が正規分布に従うと考えると、21日以内に終わる確率は約27%、24日以内なら約89%です。約束する日付を決めるときの材料になります
標準偏差は足し算できません。足すのは分散で、合計してから平方根を取ります。
つまずきやすい点と直し方
| つまずきやすい点 | 起きること | こうするとよい |
|---|---|---|
| 合流点で小さいほうのEFを採る | 全体の所要日数が短く出る | 前進計算は最大、後退計算は最小と表の上に書いておく |
| 後退計算で分岐点のLFを最大にする | TFが大きく出て、クリティカルな作業を見落とす | 後続のLSをすべて書き出してから最小を選ぶ |
| TFを作業ごとに足して使う | 同じ経路の余裕を二重に数える | 経路で共有する余裕と考え、FFも併せて見る |
| ダミー作業を計算から外す | 順序関係が抜けて日数が短く出る | 日数0の作業として前進・後退の両方に入れる |
| クリティカルパスは1本と決めつける | 同じ長さの経路を見落とす | TFが0の作業をすべて拾い、経路を全部たどる |
| 一度求めて終わりにする | 進み具合で経路が入れ替わる | 日数が変わるたびに計算し直す。TFが小さい経路も見ておく |
例題3では、Gを通る B→D→G→I→J が21日で、クリティカルパスとの差は1日です。Gが2日遅れると、こちらが最長の経路に替わります。TFが小さい作業は、クリティカルパスに準じて見ておくと安心です。
よくある質問
Q. クリティカルパスが2本以上あることはありますか? A. あります。同じ長さの最長経路が複数あれば、すべてクリティカルパスです。この記事のコードは、TFが0の作業をたどって見つかった経路をすべて表示します。
Q. 日程を短くしたいときは、どの作業から手を付ければよいですか? A. クリティカルパス上の作業です。それ以外の作業を短くしても全体の所要日数は変わりません。短くした結果、別の経路が最長になることがあるので、短縮のたびに計算し直します。
Q. 余裕日数(トータルフロート)が負になるのはどんなときですか? A. 終了の期日を先に決めて後退計算をした場合に、最短の所要日数が期日を超えていると負になります。たとえば例題3で期日を20日とすると、クリティカルパス上の作業のTFは−2になり、2日分の短縮が必要だと分かります。
Q. ガントチャートとはどう使い分けますか? A. アローダイアグラムと計算表は「どの順序で、どこに余裕があるか」を求める道具、ガントチャートは求めた日付をカレンダーに並べて共有する道具です。先に計算してからガントチャートに落とすと、線の引き直しが少なくなります。
まとめ
- クリティカルパスは所要日数の合計が最も長い経路。その長さが全体の最短の所要日数になる
- 前進計算でES・EF、後退計算でLF・LSを求め、TF=LS−ESが0の作業をつなぐ
- 合流点は、前進計算なら最大、後退計算なら最小を採る
- トータルフロートは経路で共有する余裕、フリーフロートは後続に響かない余裕。例題2のBとDで違いが出る
- ダミー作業は日数0の作業として計算に入れる。結合点の時刻でも同じ結果になる
- 三点見積もりは (o+4m+p)÷6。経路全体では分散を足してから平方根を取る
- 日数が変わったら計算し直す。TFが小さい経路も一緒に見ておく
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「クリティカルパスの求め方を例題3問で解説|最早・最遅と余裕日数の計算」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。