コラムCOLUMNS

シミュレーションの考え方と使いどころ、離散イベントからデジタルツインまで

公開日:2026年8月25日

こんにちは。Anagraftの伊藤です。

本コラムは、シミュレーションを仕事の道具として使えるようになるための記事としてまとめました。モンテカルロ法、離散イベントシミュレーション、エージェントベースモデリング、システムダイナミクスという4つの型を、それぞれ何を答えるための道具なのかという観点から順に説明し、入力データの作り方、結果の読み方、モデルを信じてよいかの確かめ方、そしてデジタルツインへの発展までを一続きの流れで扱います。

最初に、この記事が置いている軸をはっきりさせておきます。シミュレーションは、数理最適化とは目的の違う技術です。最適化は「どの案が最良か」を求めます。決定変数と目的関数と制約を数式で書き、その数式の世界の中で最良の解を出します。数式に書けたものしか扱えませんが、書けたものについては最良であることを保証します。一方のシミュレーションは、最良を保証しません。その代わり、数式では閉じない要素をそのまま持ち込めます。作業時間のばらつき、資源の奪い合いで生まれる待ち、設備の故障、人の判断、レイアウトの制約、個体どうしの相互作用。こうしたものを含んだまま時間を進めて、「この案だと実際に何が起きるか」を見せる技術です。

この違いは、実務では役割分担として現れます。配送ルートの総距離を最短にするのは最適化の仕事ですが、そのルートで走ったときに配送センターの荷捌き場が何時に詰まるかは、最適化では答えられません。人員の割付表を作るのは最適化ですが、その割付で当日に何人が待たされ、何人が諦めて帰るかはシミュレーションの領分です。どちらが優れているかではなく、問いの形が違います。

もうひとつ、シミュレーションが実務で効く理由があります。答えが分布で出ることです。「平均待ち時間は3分です」ではなく、「平均は3分で、10回に1回は12分を超えます」と言える。経営が顧客に約束するのは平均ではなく上限であることが多いので、この差は報告の使い勝手に直結します。

想定している読者は次のような方です。

  • 設備投資や人員配置の判断を、実感と経験だけで決めることに限界を感じている経営層の方
  • 需要予測やAIの導入は進めたが、その先の「では何をどう変えるか」で止まっているDX推進担当の方
  • 現場改善のアイデアを、実際に手を付ける前に効果の大きさで並べたい事業部門の方
  • 数理最適化を検討したものの、業務の実態が数式に収まらず行き詰まった経験のある方

前半(第1章から第7章)は、シミュレーションとは何をする技術かという整理から始めて、4つのモデルの型をひとつずつ扱います。後半(第8章から第16章)は、実際に使うための工程です。入力データの作り方、結果を統計的に読む作法、モデルの妥当性を確かめる手続き、計算を節約する工夫、最適化との組み合わせ方、業務領域ごとの使いどころ、道具の選び方、デジタルツインへの発展、そしてプロジェクトとしての進め方を順に扱います。

説明のための計算は、実際にPythonで動かした結果をそのまま載せました。窓口を1つ増やすと離脱率がどう動くか、稼働率を0.5から0.9へ上げると待ち時間が何倍になるか、平均が同じでばらつきの形だけを変えると結果がどう変わるか。数字は実行結果であり、コードもそのまま載せていますので、手を動かす際の出発点としても使える構成にしました。Pythonが読めなくても筋が追えるように、コードの直後には必ず結果の読み方を日本語で書いています。数式も同様に、読み飛ばしても章の主張が取れるようにしました。

取り上げた事例は、企業や研究機関、官公庁が自ら公表している一次情報で内容を確認できたものに限りました。出典は記事の末尾にまとめています。裏が取れなかった数値は、よく引用されているものであっても載せていません。デジタルツインの市場規模のように、調査会社によって推計が大きく食い違う項目は、1つの数字に決め打ちせず、食い違いごと示す形にしました。

扱わなかったことも書いておきます。数理最適化の定式化のしかたそのもの、つまり決定変数や目的関数をどう立てるかという話は、本コラムでは深入りしません。第12章でシミュレーションと組み合わせる場面に触れるにとどめています。予測モデルの作り方や時系列の手法も同様で、シミュレーションへの入力として必要な範囲までとしました。

$authorName
著者伊藤 瑛志

Anagraft(アナグラフト)合同会社 代表 AXプロジェクト顧問・支援
データサイエンティスト since 2013 BCG/ALBERT(現アクセンチュア)出身

目次

序章 予測でも最適化でもない、第三の道具

データを事業の判断に使う取り組みは、この10年でおおむね2つの方向に整理されてきました。ひとつは予測です。過去のデータから将来の需要や故障や離反を当てにいく。もうひとつは最適化です。予測された条件のもとで、コストが最小になる、あるいは利益が最大になる打ち手を数式で求める。多くの企業のデータ活用は、この2本の柱で語られています。

ところが実務では、この2本だけでは埋まらない場所が残ります。需要が予測できて、最適な生産計画も出た。それでも「その計画どおりに工場が流れるのか」は分からない。人員の最適配置が出た。それでも「その配置で当日の窓口に何人の行列ができるのか」は分からない。この隙間を埋めるのが、本コラムで扱うシミュレーションです。

なぜこの隙間が生まれるのか

予測も最適化も、扱う対象を数式の形に翻訳することで成立します。予測は入力と出力の関係を関数として表現し、最適化は目的と制約を式として表現します。翻訳できたものについては強力ですが、翻訳の過程で必ず何かが落ちます。落ちやすいのは、時間の中で積み上がる相互作用です。

作業時間が平均10分の工程が5つ並んでいるとき、5つの工程の平均を足しても、実際のリードタイムにはなりません。前の工程が遅れれば次の工程は手待ちになり、前の工程が早ければ次の工程の前に仕掛品が溜まります。作業者が2人しかいなければ、3つ目の作業は誰かが空くまで始まりません。こうした「待ち」「干渉」「順序依存」は、平均値の計算では消えてしまい、数式に書こうとしても閉じた形になりません。

シミュレーションは、この翻訳をあきらめる技術です。数式にまとめる代わりに、対象の動きをそのまま計算機の中で再現し、時間を進めて何が起きるかを観察します。答えを解くのではなく、動かして測る。だから最良の保証はありませんが、数式に収まらない現実を持ち込めます。

分布で答えが返ってくる

シミュレーションのもうひとつの性質は、結果が分布で得られることです。同じ条件でも乱数の引き方が違えば結果は変わるので、何度もまわして結果のばらつきごと受け取ります。

これは面倒に見えて、意思決定の側から見ると都合がよい性質です。設備の増設を判断するとき、知りたいのは「平均待ち時間が何分縮むか」だけではありません。「最悪の日にどこまで行列が伸びるか」「サービスレベルを95%守れる確率はどれくらいか」を知りたい。単一の数値を返す計算では、この問いに答えようがありません。分布を持っていれば、そのまま答えられます。

在庫の意思決定も同じ構造です。品切れの損失と過剰在庫の損失は釣り合っていないので、平均に合わせて発注する理由はありません。損失の非対称性に応じて、分布のどこを取るかを選ぶことになります。この「点ではなく分布で持つ」という発想は、本コラム全体を貫きます。

いま取り上げる理由

シミュレーション自体は新しい技術ではありません。離散イベントシミュレーションの基本的な考え方は1960年代には確立しており、システムダイナミクスも同じ頃に生まれています。それでもいま改めて実務の道具として取り上げる理由は、3つあります。

第一に、データが揃ってきたことです。シミュレーションの品質は入力データで決まりますが、これまでは作業時間や搬送時間の実測値が現場になく、モデルを作っても仮定の塊にしかなりませんでした。生産管理システム、倉庫管理システム、勤怠、IoTセンサー、位置情報。実測のログが蓄積された結果、入力を推測ではなく実データで置けるようになってきました。

第二に、計算資源が安くなったことです。シミュレーションは統計的な推定なので、条件をひとつ比べるだけでも何十回、何百回とまわす必要があります。かつては計算時間そのものが制約でしたが、いまはクラウド上で並列に流せます。第12章で扱うシミュレーション最適化のように、シミュレーションを何千回も評価関数として呼び出す使い方も現実的になりました。

第三に、デジタルツインという形で、モデルを現場と接続する道筋ができたことです。従来のシミュレーションは、設計や投資判断のときに一度作って終わりでした。現場の実データを継続的に取り込み、モデルを実物と同期させ続ける構成が取れるようになったことで、シミュレーションは「作って終わるもの」から「運用し続けるもの」に変わりつつあります。これは第15章で扱います。

本コラムを通じて一貫して伝えたいのは、シミュレーションが「最適化の代わり」でも「最適化の劣化版」でもなく、答えるべき問いの形が違う別の道具だということです。次章では、その違いを正面から整理します。

Anagraftでは、AIプロジェクトの構想・課題設計から、データ分析・機械学習モデルの開発、AI人材の育成まで一貫したご支援を行っています。会社概要・ご支援内容の詳細は、以下の資料からご覧いただけます。

Anagraft 会社概要資料(ダウンロードページへ)

全体の地図

本コラムの構成です。前半が考え方と4つのモデルの型、後半が実際に使うための工程と発展です。

タイトル扱う内容
第1章シミュレーションとは何をする技術か、最適化との違い予測・シミュレーション・最適化の役割分担、得意と不得意
第2章4つのモデルの型、どれを選ぶかは問いで決まる4つの型の位置づけ、問いから型を選ぶ対応表、粒度の決め方
第3章モンテカルロ法、不確実性を数え上げて意思決定に渡す不確実性の伝播、分位点での意思決定、感度分析
第4章離散イベントシミュレーションの仕組み、時計とイベントと資源時計の進め方、エンティティと資源とキュー、窓口数の評価
第5章待ち行列の理屈、稼働率とばらつきが待ち時間を決めるリトルの法則、利用率の非線形性、式で解ける範囲の境界
第6章エージェントベースモデリング、個の行動から全体が立ち上がる創発、しきい値モデル、普及の臨界点
第7章システムダイナミクス、ストックとフローで構造の癖を読むフィードバックループ、時間の遅れ、ブルウィップ効果
第8章入力のモデリング、データから分布を決める分布の選び方、現場データの落とし穴、データが無いときの扱い
第9章出力の分析、何回まわせば信じてよいのか反復と信頼区間、立ち上がり期間の除去、比較は差で見る
第10章検証と妥当性確認、モデルを信じるための手続き検証と妥当性確認の違い、確かめ方の一覧、適用範囲の宣言
第11章分散減少法と実験計画、少ない計算で差を見分ける共通乱数法、対称変量法、要因計画とスクリーニング
第12章シミュレーション最適化、評価と探索を組み合わせる最適化との接続、メタモデル、ランキングと選択
第13章業務課題別の使いどころ、どの部門の何に効くか製造・物流・小売・金融・医療・人員計画での適用
第14章ツールの地図とPythonでの実装商用ツールとオープンソースの使い分け、SimPyの書き方
第15章デジタルツイン、モデルを現場につなぐ定義と成立条件、同期の設計、段階的な進め方
第16章プロジェクトとして進める、導入の順序と落とし穴問いの決め方、体制、運用への引き継ぎ、失敗の型

第3章から第7章は4つのモデルの型をそれぞれ扱う章です。関心のある型から読んでも筋が通るようにしましたが、第8章以降の工程はどの型にも共通しますので、実際に手を付ける段階では通して読んでいただく想定です。

第1章 シミュレーションとは何をする技術か、最適化との違い

シミュレーションという言葉は、日常語としても技術用語としても使われるため、意味の幅がとても広くなっています。経営会議で「シミュレーションしてみます」と言うとき、多くの場合それは表計算ソフトで数字を入れ替えて合計を見ることを指します。一方で、工場の設備投資を検討する部署が同じ言葉を使うとき、それは装置ごとの処理時間や搬送の待ちを時間軸に沿って再現する専用のモデルを指しています。同じ言葉が、片方では四則演算の入れ替えを、もう片方では数千行のモデルと数時間の計算を意味しているわけです。この章では、後者の意味での技術としてのシミュレーションが何をしているのかを、はっきりした形で定義します。

定義をはっきりさせる目的は2つあります。1つは、この技術が解ける問いと解けない問いの境界を引くことです。境界がぼやけたまま導入すると、期待した答えが出ない使い方をしてしまい、モデルそのものが信用を失います。もう1つは、隣接する技術との役割分担を明確にすることです。特に数理最適化との関係は、実務の現場でもっとも混同されやすく、しかしもっとも実益のある区別です。両者は競合する技術ではなく、問いの形が違うために担当範囲が違う技術です。

本章では、まずシミュレーションの定義を置き、次に数理最適化が何をしているのかを並べ、両者を1つの表で対比します。そのうえで予測を加えた3者の役割分担を整理し、解析的に解けるかどうかという判断基準を示します。最後に、得意なことと不得意なことを列挙し、業務に近い具体の対比を3組挙げて章を閉じます。以降の章はすべて、この章で置いた対比の上に乗っています。

シミュレーションとは、計算機の中で時間を進めることである

技術としてのシミュレーションは、次の3つの動作をまとめた仕組みです。第1に、現実のシステムを模したモデルを計算機の中に作ります。第2に、そのモデルの中で時間を進め、状態がどう変わっていくかを再現します。第3に、入力の条件を変えて何度も時間を進め、結果を比べます。この3つ目まで含めて初めてシミュレーションになります。1回動かして数字を眺めるだけなら、それは計算であってシミュレーションとは呼びにくいものです。比べるという行為が入るからこそ、意思決定の道具になります。

ここでいうモデルとは、現実の全部を写したものではありません。問いに答えるために必要な部分だけを選んで写したものです。たとえば物流センターの荷捌き能力を知りたいのであれば、トラックの到着時刻、荷降ろしに要する時間、バースの数、作業者の人数、荷物の仕分け先といった要素は必要ですが、トラックの塗装色や作業者の氏名は不要です。何を入れて何を捨てるかの判断そのものが、モデル作りの本体です。この取捨選択は問いによって変わるので、同じ物流センターでも、聞きたいことが変われば別のモデルになります。

時間を進めるという部分も、もう少し具体的に見ておきます。モデルの中には状態を表す変数の集まりがあります。バースが空いているか塞がっているか、待っているトラックが何台か、仕分け済みの荷物が何個か、といった変数です。シミュレーションは、あらかじめ決めた規則に従って時刻を先へ動かし、そのたびに状態変数を更新します。時刻の進め方には、一定の刻み幅で進める方式と、次に何かが起きる時刻まで一気に飛ばす方式があり、後者が離散イベントシミュレーションの中核になります。この仕組みは第4章で詳しく扱います。

そして条件を変えて比べる部分が、実務でもっとも価値を生みます。バースを1つ増やしたらどうなるか、作業者を朝の2時間だけ2人増やしたらどうなるか、トラックの到着を予約制にしたらどうなるか。こうした問いに対して、それぞれの条件でモデルを動かし、待ち時間や滞留台数の変化を並べて見せる作業をWhat-if分析と呼びます。シミュレーションは本質的にWhat-if分析の装置です。答えを1つ出す機械ではなく、複数の選択肢に点数を付けて並べる機械だと考えると、位置づけを間違えにくくなります。

この「答えを出すのではなく選択肢を評価する」という性格は、導入時の期待値の置き方に直結します。モデルを作れば最適な設備台数が自動的に出てくると期待されることがありますが、シミュレーション単体はそういう働きをしません。台数を指定すれば、その台数のときに何が起きるかを教えてくれるだけです。最良の台数を探すには、候補を並べて総当たりするか、探索の仕組みを外側に付け足す必要があります。その組み合わせ方は第12章で扱います。

数理最適化は、数式の中で最良を探す

数理最適化は、問題を3つの部品に分解して数式で書き、その数式が表す範囲の中で最良の解を求める技術です。3つの部品とは、決定変数、目的関数、制約です。決定変数は自分が決められるもの、たとえばどの倉庫からどの店舗へ何個運ぶか、どの従業員をどのシフトに入れるか、といった値です。目的関数は良し悪しを測る尺度で、総輸送費や総残業時間のように、小さくしたい、あるいは大きくしたい1つの数値です。制約は守らなければならない条件で、倉庫の在庫量を超えて出荷できない、1人の従業員に連続勤務させられない、といった関係を不等式や等式で書きます。

形式的には \( \min_{x \in X} f(x) \) と書けます。ここで \( x \) が決定変数のベクトル、\( f \) が目的関数、\( X \) が制約を満たす解の集合です。この形に書けたとき、線形計画や混合整数計画のソルバーは、集合 \( X \) の中で \( f \) を最小にする \( x \) を探し出します。しかも多くの場合、単に良い解を見つけるだけでなく、これ以上良い解は存在しないことを数学的に示すか、あるいは最良解との差が何パーセント以内かという保証を付けて返します。この保証が数理最適化のもっとも強い性質です。

その代償として、数式に書けたものしか扱えないという制限があります。目的関数と制約は、決定変数から一意に値が決まる関数でなければなりません。ところが現実の業務には、そう書けない要素がたくさんあります。トラックが何時に着くかは日によってばらつきます。前の作業が長引けば次の作業は待たされます。装置は時々止まります。同じ通路を2台のフォークリフトが使おうとすれば干渉します。作業者は状況を見て順番を入れ替えます。これらは決定変数の関数として素直に書けません。書こうとすると、変数の数が爆発するか、大胆な平均化を持ち込んで現実から離れるかのどちらかになります。

実務で数理最適化を使うとき、この平均化は必ずどこかで行われています。たとえば生産計画のモデルでは、ラインの能力を「1時間あたり120個」という定数で置くのが普通です。しかし実際のラインは、段取り替えの間は止まり、上流の欠品で待たされ、ときどき不良で手戻りします。定数の120という数字は、それらをならした結果として置かれた値です。ならした世界の中では最良の計画が出ますが、その計画がならしていない現実の中で成立するかどうかは、別の方法で確かめる必要があります。ここにシミュレーションの出番があります。

誤解を避けるために書いておくと、数理最適化が現実を単純化しすぎる技術だという主張ではありません。むしろ逆で、数式に落とし込めた範囲については、人間が手作業で組んだ案よりはるかに良い解を、はるかに短時間で出します。輸送費の削減、シフトの公平化、設備の割付といった領域で成果が積み上がっているのは、その保証の強さゆえです。問題は、どちらの技術が優れているかではなく、目の前の問いがどちらの形をしているかを見分けることです。

2つを並べて見る

ここまでの内容を、観点ごとに並べて整理します。表の各行は、実際のプロジェクトで「どちらの技術を使うか」を判断するときに確認する項目でもあります。

観点数理最適化シミュレーション
問いの形どの案が最良かこの案だと何が起きるか
扱える対象決定変数の関数として数式に書けるもの規則として記述できるものすべて。ばらつき、待ち、干渉、順序依存、故障、人の判断を含む
答えの性質最良の解と、それを裏づける最適性の証拠指定した条件のもとで観測された成績の集まり
ばらつきの扱い原則として平均値や代表値に置き換える。確率計画やロバスト最適化で部分的に扱う乱数として直接持ち込み、結果も分布として出す
保証あり。大域最適、あるいは最適性ギャップの上界なし。試した候補の中で相対的に良かったものが分かるだけ
主な出力決定変数の値。輸送量、割付表、発注量など性能指標の分布。待ち時間、滞留数、稼働率、納期遵守率など
探索の仕方数式の構造を利用して効率よく解空間を絞る候補を指定して1つずつ評価する。探索は外側の仕組みが担う
計算資源の使われ方1回の求解に集中する候補数と反復回数の掛け算で増える
向く場面選択肢が膨大で、評価の仕方は単純に書ける場面選択肢は少数だが、1つの案の帰結が単純に書けない場面

この表でとくに注目してほしいのは、最後の「向く場面」の行です。数理最適化が力を発揮するのは、取りうる案が天文学的に多く、人手では数え上げられない場面です。100拠点から500店舗への配送計画なら、組み合わせの数は現実的に列挙できません。しかし1つの案の良し悪しは、輸送距離に単価を掛けて足すだけで測れます。評価は簡単で、探索が難しい構造です。

シミュレーションが力を発揮するのは、その逆の構造です。検討している案は、多くても数十通りです。バースを3つにするか4つにするか5つにするか、シフトを2交代にするか3交代にするか、といった粒度です。ところが1つの案の帰結を測るのが難しい。距離に単価を掛けるようには計算できず、時間を進めてみないと待ち行列がどこにできるか分からない。評価が難しく、探索は簡単な構造です。

したがって、迷ったときの見分け方は「案を数え上げられるか」と「案の良し悪しを式で測れるか」の2つを問うことです。数え上げられないが式で測れるなら数理最適化、数え上げられるが式で測れないならシミュレーション、両方とも難しいなら両者を組み合わせることになります。

横軸を「案の数」

予測、シミュレーション、最適化の3者がどう連鎖するか

実務では、この2つに予測を加えた3者が連なって働きます。3者はそれぞれ別の問いに答えます。予測は「何が起きそうか」に答えます。来月の需要、来週の来店客数、装置の故障確率といった、自分では決められない外部の条件を数値として与えるのが役割です。シミュレーションは「この案だと何が起きるか」に答えます。与えられた外部条件のもとで、ある運用案を採ったときに社内で何が起きるかを再現します。最適化は「どの案が最良か」に答えます。評価の仕方が決まっているとき、その尺度で最も良い案を選び出します。

典型的な連鎖は、予測から始まって最適化で終わる向きです。まず需要予測で来期の出荷量の見通しを立てます。次に、その出荷量を前提として、倉庫の運用案をいくつか作りシミュレーションで評価します。ここで案ごとの待ち時間や残業時間の分布が出ます。最後に、その評価結果を目的関数や制約の形に整えて最適化にかけ、投資額と運用コストの合計が最小になる案を選びます。前段の出力が後段の入力になる、素直な流れです。

もう1つの重要な連鎖は、逆向きです。最適化で解を出したあと、その解をシミュレーションに入れて検証します。前節で書いたとおり、最適化のモデルはどこかで平均化を行っています。平均化した世界での最良解は、ばらつきのある現実の中では脆いことがあります。たとえば、稼働率が98パーセントになるまで設備を詰め込む計画は、計算上は費用最小ですが、少しでも到着が乱れると待ちが急増します。この脆さは平均だけを見る式には現れず、時間を進めてみて初めて見えます。最適化の解をそのまま現場に渡す前に、シミュレーションで揺さぶってみる工程を挟むと、こうした落とし穴を事前に潰せます。

3者を分けて考えることの効用は、失敗の原因を切り分けられる点にもあります。導入したモデルが役に立たなかったとき、原因は3つのどこかにあります。予測が外れていたのか、シミュレーションのモデルが現実を写せていなかったのか、最適化の目的関数が現場の価値観とずれていたのか。3者を一体の「AIによる意思決定システム」として作ってしまうと、この切り分けができず、どこを直せばよいか分からなくなります。役割ごとに分けて作り、それぞれの精度を別々に測れるようにしておくことが、長く使えるモデルの条件です。

なお、予測とシミュレーションの境目は、外部条件を扱うか内部の振る舞いを扱うかで引くのが実用的です。来月の受注件数は自社では決められないので予測の担当です。その受注件数を処理するときに事務部門で何日の滞留が起きるかは、自社の運用規則の帰結なのでシミュレーションの担当です。この線の引き方は絶対的なものではありませんが、担当と責任を分けるうえで機能します。

解析的に解けるかどうかが分かれ目になる

シミュレーションを使うかどうかを判断するもう1つの基準が、閉じた式で答えが出るかどうかです。閉じた式とは、入力を代入すれば計算だけで答えが出る数式のことです。もし問いに対する閉じた式が存在するなら、シミュレーションは不要です。式に数字を入れれば、誤差もばらつきもなく答えが出るからです。何時間も計算機を回して近似値を得る理由がありません。

待ち行列の分野には、こうした閉じた式がいくつも用意されています。到着が完全にランダムで、サービス時間も指数分布に従い、窓口が1つだけという条件のもとでは、平均の待ち人数は \( L = \rho / (1 – \rho) \) という短い式で求まります。ここで \( \rho \) は利用率、つまり到着率をサービス率で割った値です。この条件を満たす場面であれば、モデルを組む前に電卓で答えが出ます。窓口の数を変えたときの式や、待ち時間の分布の式も整備されています。詳しくは第5章で扱います。

問題は、この閉じた式が使える条件がかなり狭いことです。式が成り立つには、到着の仕方やサービス時間の分布に強い仮定が必要で、しかも工程が1つだけ、資源の取り合いがない、優先順位の入れ替えがない、といった単純さが前提になります。実務のシステムは、たいてい工程が連なっていて、複数の工程が同じ作業者を共有し、緊急案件が割り込み、装置が時々止まります。こうなると式は立たなくなります。厳密には状態を全部並べれば方程式は書けますが、状態の数が現実的に扱えない規模になり、解けません。

式が立たない、あるいは立っても解けない。この2つのいずれかに当てはまったときに、シミュレーションの出番になります。逆に言えば、当てはまらないうちは使うべきではありません。プロジェクトの初期に、まず解析的な近似で当たりを付け、そこで答えが十分なら終わりにする判断は、まったく正しいものです。シミュレーションは作るのにも維持するのにも手間がかかる道具なので、安く済むなら安く済ませたほうがよいのです。

実務的には、両方を併用する進め方が有効です。まず閉じた式で概算を出し、その値を目安として持っておきます。次にシミュレーションのモデルを作り、式が成り立つ単純な条件に設定して動かします。このとき、シミュレーションの結果が式の値とおおむね一致すれば、モデルの土台に大きな間違いがないことの証拠になります。一致を確認したうえで、複数工程や割り込みといった現実の要素を1つずつ足していきます。この手順は、モデルの信頼を積み上げるうえで有効で、第10章の妥当性確認とも関わります。

シミュレーションが得意なこと

第1に、平均だけでなく分布が出ます。これはこの技術のもっとも実務的な利点です。平均待ち時間が8分というモデルがあったとして、そこから読み取れることは限られています。8分の背後には、ほとんどの客が3分で済み、ときどき40分待つ人が出る世界もあれば、全員が判で押したように7分から9分の間に収まる世界もあります。クレームが起きるのは前者で、平均は同じでも対策はまったく違います。シミュレーションは1回の実行ごとに1つの値を出すので、何百回も回せば分布が手に入り、95パーセンタイルや最悪の1パーセントがどのあたりかを直接読み取れます。サービス水準を「平均何分以内」ではなく「95パーセントの客が何分以内」と定義している組織にとって、この出力はそのまま使えます。

第2に、ボトルネックの所在が見えます。全体の処理能力が足りないという事実は、実績データからも分かります。しかし、どの工程が足を引っ張っているかは、実績を眺めるだけでは分かりにくいものです。稼働率が高い工程が犯人とは限らず、その手前の工程が断続的に供給を止めているせいで、下流が待たされている場合もあります。シミュレーションは内部の状態をすべて記録できるので、資源ごとの稼働率、待ち行列の長さの時間変化、ブロッキングの発生回数といった数値を工程単位で取り出せます。改善の順番を決める根拠として使えます。

第3に、実物を止めずに試せます。稼働中のラインで作業手順を変えてみるには、生産を止めるか、失敗のリスクを負って本番で試すかしかありません。空港の保安検査の並び方を変えてみるには、実際に旅客を並ばせる必要があります。モデルの中であれば、こうした試行を何度でも、業務を止めずに行えます。この点は、改善のサイクルを速く回すうえで大きな差になります。

第4に、まだ存在しない設備や運用も試せます。実績データから学ぶ手法は、過去に起きたことの範囲でしか答えられません。導入したことのない自動倉庫を入れたらどうなるか、まだ開設していない店舗の混雑はどうなるか、という問いには、実績データだけでは答えようがありません。シミュレーションは、規則として書ければ動かせるので、存在しないものを前提にした計算ができます。設備投資の判断や新拠点の設計で使われる理由がここにあります。

第5に、失敗のコストがゼロです。モデルの中で在庫を切らしても、誰も困りません。極端な条件を入れて壊してみることもできます。需要が3倍になったら、装置が2台同時に止まったら、熟練者が1人抜けたら。こうしたストレス条件での挙動を先に知っておくと、実際にその事態が起きたときの備えを用意できます。平常時の性能を測る使い方よりも、異常時の弱点を洗い出す使い方のほうが、投資対効果が高いことも少なくありません。

第6に、関係者への説明がしやすい点も見逃せません。数式で最適解を示されても、現場の担当者にはその妥当性を検証する手立てがありません。一方、シミュレーションはモデルの中で起きたことを時系列で追えるので、なぜこの案だと待ちが増えるのかを、具体的な経過として示せます。動きを可視化する機能を持つツールも多く、合意形成の場で使えます。モデルが受け入れられるかどうかは、精度と同じくらい説明可能性に左右されます。

シミュレーションが不得意なこと

第1に、最良の保証がありません。10個の案を試して最も良かった案が分かっても、11個目に試していない案がもっと良い可能性は残ります。数理最適化のように「これ以上はない」と言い切ることはできません。したがって、経営に対して「最適な人員配置はこれです」と報告するのは、シミュレーション単体の結果としては言い過ぎです。「検討した5案の中ではこの案が最も安定していました」という言い方が、出力に忠実な報告になります。

第2に、入力の質に結果が引きずられます。到着間隔の分布、処理時間の分布、故障の頻度といった入力を、どこかから決めなければモデルは動きません。この入力が現実とずれていれば、どれだけ精巧に組んだモデルでも出力はずれます。しかも厄介なことに、ずれていても出力はもっともらしい数字として出てくるので、間違いに気づきにくいのです。入力をどう決めるかは技術的にも実務的にも重い話題で、第8章をまるごと使って扱います。

第3に、計算時間がかかります。1回の実行が数秒で終わるモデルでも、乱数による揺らぎを均すために30回繰り返し、それを20案分行えば600回になります。1回が1分なら10時間です。実務ではモデルの規模が大きくなりがちで、1回の実行に数分から数十分かかることも珍しくありません。検討の途中でモデルを直すたびに再実行が必要になるので、計算時間はプロジェクトの進行速度を直接決めます。この負担を減らす技術が第11章の分散減少法と実験計画です。

第4に、モデルの信頼を得るまでに手間がかかります。作った本人以外にとって、モデルは中身の見えない箱です。出てきた数字が現実と合っているかどうかを、現場の人が納得する形で示さなければ、意思決定には使ってもらえません。過去の実績と突き合わせる、現場の担当者に動きを見てもらって違和感がないか確認する、極端な入力を入れて常識的な反応をするか調べる。こうした手続きを踏む必要があり、モデル構築そのものより時間がかかることもあります。第10章の主題です。

第5に、変数が多いと組み合わせを試しきれません。検討したい要因が10個あり、それぞれに2つの水準しかないとしても、全組み合わせは1024通りです。各条件で30回ずつ繰り返せば3万回を超える実行になります。要因が20個になれば100万通りを超え、総当たりは不可能です。したがって、どの条件を試すかを賢く選ぶ必要が出てきます。この設計を扱うのが実験計画法で、これも第11章に置いています。

第6に、乱数による差と本当の差を取り違える危険があります。案Aの平均待ち時間が8.2分、案Bが8.5分だったとして、この0.3分の差が案の違いによるものなのか、たまたま引いた乱数の違いによるものなのかは、そのままでは分かりません。繰り返し回数を増やし、信頼区間を計算して初めて判断できます。この点を軽視して1回だけの実行結果で優劣を語ると、次に回したときに逆転して、モデルへの信頼を失います。第9章で正面から扱います。

業務に近い3組の対比と、この章のまとめ

ここまでの区別を、業務でよく出てくる場面に当てはめて確認します。以下は手法の性質から導かれる一般的な整理であり、特定の企業の事例ではありません。1組目は配送です。どの車両がどの店舗をどの順で回れば総走行距離が最短になるか、という問いは数理最適化の領域です。訪問順の組み合わせは店舗数が増えるほど爆発的に増え、人手では数え上げられません。一方で1つのルート案の良し悪しは、区間ごとの距離を足すだけで測れます。評価が簡単で探索が難しい構造なので、最適化が向きます。これに対して、その配送計画のもとで配送センターの荷捌き場が何時に詰まるかは、シミュレーションの領域です。トラックの到着はばらつき、荷降ろしにかかる時間もばらつき、バースが埋まっていれば後続は待ちます。待ちの発生は到着の順序とタイミングに依存し、足し算では出ません。

2組目は人員です。必要人数を満たしつつ、労務規則を守り、総人件費を最小にする割付表を作る問いは数理最適化の領域です。従業員とシフト枠の組み合わせは膨大ですが、費用も規則違反も線形の式で書けるため、混合整数計画として扱えます。これに対して、その割付表で運用したときに当日どれだけの人が待たされるかは、シミュレーションの領域です。窓口が3つ空いていても、来客が同じ時間帯に集中すれば待ちは発生します。手続きに要する時間は人によって違い、時間のかかる1件が後続を止めます。割付表という入力を与えたときの帰結を、時間を進めて確かめる必要があります。

3組目は生産です。どの製品を何個、どの期間に作れば、需要を満たしつつ在庫費用と段取り費用の合計が最小になるかという問いは数理最適化の領域です。これに対して、その生産数量を実際にそのラインが流せるかどうかは、シミュレーションの領域です。計画では能力を1時間あたり120個と置いていても、段取り替えの時間、上流の欠品による停止、不良の手戻り、作業者の休憩が重なると、実際の産出は計画に届かないことがあります。計画が実行可能かどうかは、工程の連なりと変動を持ち込んで動かしてみて初めて判定できます。

3組に共通する構造があります。最適化が担当しているのは「何をするかを決める」部分で、シミュレーションが担当しているのは「決めたとおりにやったとき現場で何が起きるか」の部分です。前者は紙の上の話で、後者は時間の中の話です。紙の上で辻褄が合っている計画が、時間の中で破綻することは珍しくありません。逆に、時間の中でうまく回ることが確認できても、それが最も安い案かどうかは分かりません。両者を行き来させることで、初めて実行可能かつ経済的な案にたどり着けます。この行き来を明示的な仕組みにしたものが、シミュレーション最適化と呼ばれる手法群です。案の評価をシミュレーションに任せ、次にどの案を試すかの判断を探索アルゴリズムに任せる構成をとります。評価が高価なので、少ない試行で良い案に近づく工夫が必要になり、それが手法の中身です。第12章で扱います。

「予測」「シミュレーション」「最適化」の3つのボックスを横に並べ

この章では、シミュレーションを「現実のシステムをモデルとして計算機の中に作り、時間を進めて振る舞いを再現し、条件を変えて結果を比べる技術」と定義し、答えを出す装置ではなく選択肢を評価する装置だと位置づけました。数理最適化との違いは、数式に書けるものしか扱えないが最良を保証する側と、最良は保証しないが数式に閉じない要素をそのまま持ち込める側という対比に整理できます。判断の目安は、案を数え上げられるかと、案の良し悪しを式で測れるかの2点です。

以降の章は、この土台の上に順に積んでいきます。第2章でモデルの4つの型を示し、問いによってどれを選ぶかを整理します。第3章から第7章までで、モンテカルロ法、離散イベントシミュレーション、待ち行列の理屈、エージェントベースモデリング、システムダイナミクスを1つずつ扱います。第8章から第11章までは、入力の決め方、出力の読み方、モデルを信じるための検証と妥当性確認、少ない計算で差を見分ける方法という、結果の信頼性を支える技術をまとめます。第12章でシミュレーションと最適化を接続し、第13章で業務課題別の使いどころを整理します。第14章でツールとPythonでの実装、第15章でデジタルツインへの発展、第16章でプロジェクトとしての進め方を扱います。

この章を深めたい方への参考書籍

OR入門(宮川公男、丸善出版)
オペレーションズ・リサーチの全体像を1冊で見渡せる入門書です。シミュレーションが数理最適化や待ち行列とどう並ぶ道具なのかを、分野の地図として確かめたいときの出発点になります。

第2章 4つのモデルの型、どれを選ぶかは問いで決まる

第1章では、シミュレーションが「最適な答えを計算で出す」道具ではなく、「決める前に試してみる」道具であることを述べました。ここから先に出てくる疑問は、その「試す」をどう作るのかという話です。同じ現場を対象にしても、作り方はひととおりではありません。設備を1台ずつ数えて時間の流れを追う作り方もあれば、設備の台数などいっさい持たずに全体の在庫量だけを連続量として追う作り方もあります。どちらも「シミュレーション」と呼ばれますが、答えられる問いはまったく違います。

この章では、実務で使われるモデルの型を4つに整理し、それぞれの守備範囲を並べます。各型の中身、つまりどう組み立ててどう動かすのかは第3章から第7章で個別に扱いますので、ここでは深入りしません。この章の役割は、目の前の課題に対してどの型を持ち出すべきかを判断できる地図を渡すことです。型を間違えると、作ったモデルはそれらしく動くのに、肝心の問いには答えられないものになります。しかもその失敗は、モデルが完成して数字が出てきた後になってようやく気づくことが多く、そこまでに費やしたデータ収集と実装が丸ごと無駄になります。

順序としては、まず4つの型の輪郭を示し、次に型を区別している4本の軸を明示します。軸が分かれば、新しい型や亜種が出てきたときにも自分で位置づけられるようになります。そのうえで、問いの形から型を引ける対応表を置き、最後に粒度の決め方、型の組み合わせ方、よくある選び間違いを扱います。

4つの型は、時間と個体の扱い方で分かれている

最初に4つの名前を挙げておきます。モンテカルロ法、離散イベントシミュレーションエージェントベースモデリングシステムダイナミクスの4つです。この4つが定番として扱われているのは、恣意的な分類だからではなく、「時間をどう進めるか」と「対象を何の単位で見るか」という2つの根本的な設計判断の組み合わせが、ちょうどこの4つに落ちるからです。

モンテカルロ法は、時間を進めません。不確実な入力を確率変数として置き、乱数でその値を大量に振り、そのつど結果を計算して、出てきた結果の分布を見ます。1回の試行の中に「時刻」という概念がなく、入力から出力への計算が1回走るだけです。工期の見積もり、投資の採算、在庫の欠品確率のように、「関係式そのものは分かっているが、入力が振れる」問題に向きます。第3章で扱います。

離散イベントシミュレーションは、時間を進めます。ただし一定の刻みで進めるのではなく、「次に何かが起きる時刻」まで一気に飛ばします。客が到着した、加工が終わった、故障した、といった事象が起きた瞬間だけ状態が変わり、その間は何も起きていないので計算する必要がないという発想です。個々の対象が窓口や設備といった限りある資源を奪い合い、空くまで待たされる、という構造をそのまま表現できます。工場のライン、コールセンター、病院の外来、物流センターの構内動線が典型です。第4章で扱い、そこで生じる待ちの理屈は第5章で扱います。

エージェントベースモデリングも時間を進めますが、記述の中心は資源ではなく主体です。個々の主体、たとえば顧客・従業員・車両・世帯に、それぞれ行動ルールと状態を持たせ、周囲の主体や環境を見ながら判断させます。全体の挙動は、上から与えるのではなく、個々の判断の積み重ねから立ち上がってきます。口コミの広がり、値上げに対する離反の連鎖、避難行動、地域内の店舗選択のように、「隣が動いたから自分も動く」という相互作用が結果を左右する問題に向きます。第6章で扱います。

システムダイナミクスは、個体を数えません。対象を「たまるもの」と「流れるもの」に抽象化し、在庫・人員・資金・信頼といった量を連続的なストックとして扱い、その増減を微小な時間刻みで積み上げていきます。関心はループにあります。売上が増える、増員する、教育に手が回らなくなる、品質が落ちる、売上が減る、といった具合に、原因と結果が輪になっている構造を明示的に描き、その輪が時間をかけてどう効いてくるかを見ます。第7章で扱います。

ここで注意しておきたいのは、この4つは「難易度の順」でも「新しさの順」でもないという点です。エージェントベースモデリングが最も新しく、最も表現力が高いから最上位である、といった序列は存在しません。表現力が高いということは、その分だけ決めなければならない前提が多く、検証すべき箇所が増えるということでもあります。問いに対して過不足のない型を選ぶことが目的であって、豊かな型を選ぶことが目的ではありません。

4本の軸に置くと、型の違いがそのまま守備範囲の違いになる

4つの型を区別している軸を明示します。軸は4本です。第1に、時間を扱うかどうか。第2に、状態が飛び飛びに変わるか、なめらかに変わるか。第3に、個体を1つずつ数えるか、集計した量で見るか。第4に、確率を含むか、含まないか。この4本で位置づけると次のようになります。

時間を扱うか 状態の変わり方 見る単位 確率の扱い
モンテカルロ法 扱わない(静的)
1回の試行に時刻がない
該当しない
入力から出力を1回計算する
多くは集計量
個体を並べることもある
確率的
確率がなければ成立しない
離散イベント
シミュレーション
扱う(動的)
次の事象の時刻へ飛ぶ
離散
事象の瞬間だけ変わる
個体ベース
1件ずつ追跡する
確率的が基本
到着とサービス時間が振れる
エージェントベース
モデリング
扱う(動的)
一定刻みで進めることが多い
離散
主体ごとの状態が切り替わる
個体ベース
主体ごとに属性を持つ
確率的が基本
判断や接触に乱数が入る
システムダイナミクス 扱う(動的)
微小な刻みで積み上げる
連続
ストックがなめらかに増減する
集計ベース
個体を区別しない
決定的が基本
確率を後から足すこともできる

第1の軸、時間を扱うかどうかが、実務上いちばん効きます。時間を扱わないモンテカルロ法は、作るのも回すのも軽い代わりに、「順番」や「待ち」を表現できません。設備が空くまで次の仕事が始められない、という関係は、時刻を持たないモデルには書けないからです。逆に言えば、順番も待ちも結果に効かない問題であれば、時間を持ち込むのは無駄な作り込みです。

第2の軸、状態が飛ぶか連続かは、対象を数えられるかどうかとほぼ同じ意味を持ちます。窓口に並んでいる人数は3人と4人の間を取りません。飛び飛びに変わります。一方、タンクの液量や現金残高、組織全体の熟練度といった量は、なめらかに変わると考えたほうが扱いやすい。前者を連続量で近似すると、待ち行列に特有の振る舞い、たとえば混雑が非線形に悪化する現象が消えてしまいます。後者を1単位ずつ数えると、意味のない精密さのために計算時間を払うことになります。

第3の軸、個体か集計かは、個体差が結果を左右するかどうかで決まります。全員が同じ行動をとるなら、1人ずつ持つ必要はありません。平均的な1人を代表として置き、人数を掛ければ済みます。逆に、感度の高い一部の顧客だけが先に離反し、その離反が周囲に伝わって全体が動く、という構造であれば、平均的な顧客を1人置いても現象そのものが再現できません。「ばらつきがある」ことと「ばらつきが結果を決める」ことは別で、後者のときだけ個体ベースが必要になります。

第4の軸、確率を含むかどうかは、他の3軸ほど排他的ではありません。システムダイナミクスは決定的に組むのが基本ですが、需要にノイズを乗せることはできますし、離散イベントシミュレーションをあえて確率なしで動かして、理論値との突き合わせに使うこともあります。この軸は「型を分ける軸」というより、「どの型でも決めなければならない設計項目」と捉えたほうが実務に合います。

横軸を「集計ベース←→個体ベース」

問いの形が決まれば、選ぶべき型はほぼ決まる

実務で型を選ぶときの手順は単純です。「何を知りたいのか」を1文で書き出し、その文の形から引く。以下の対応表は、よく持ち込まれる問いの形を左に、適した型と理由を右に置いたものです。

問いの形 適した型 理由
この投資は最悪どこまで振れるか モンテカルロ法 知りたいのは結果の分布の裾であって、途中の順番ではない。時間を持ち込む必要がない
このラインは1日に何個流せるか 離散イベント
シミュレーション
能力は最も遅い工程と、故障や段取り替えによる停止の重なり方で決まる。設備の占有と解放を時刻つきで追う必要がある
窓口を1つ増やすと待ち時間はどう変わるか 離散イベント
シミュレーション
待ちは資源の奪い合いから生じる。単純な系なら待ち行列の公式で当たりをつけ、実際の運用ルールを入れて確かめる
値上げしたとき顧客はどう離れるか エージェントベース
モデリング
離反は一律に起きず、感度の高い層から始まって周囲に伝わる。個体差と相互作用が結果を決める
採用してから戦力になるまでの遅れは全体にどう効くか システムダイナミクス 関心はループと遅れにある。人員というストックの積み上がり方と、教育負荷を通じた跳ね返りを見たい
在庫の発注方策を変えると年間費用はどうなるか 離散イベント
+モンテカルロ法
需要の振れ方を確率で与え、発注と入荷のタイミングは時刻つきで追う。両方が要る
新製品はどこまで普及するか エージェントベース
またはシステムダイナミクス
個々の採用判断の連鎖を見たいなら前者。全体の普及率の曲線だけでよいなら後者で足りる
複数の候補案のうち、どれがいちばん良いか いずれかの型
+比較の手続き
型そのものより、何回まわして差を判定するかが本題になる。第9章と第11章で扱う

この表を使うときの注意が1つあります。持ち込まれる問いは、たいてい上の左列のように整った形をしていません。「工場の生産性を上げたい」「顧客離れを何とかしたい」といった、目的の形で来ます。目的のままでは型を引けないので、「そのために何が分かればよいか」を1段掘って、答えの単位が言える形にする必要があります。生産性を上げたい、の下にあるのが「ボトルネックがどの工程か知りたい」なのか「増設したときの投資回収年数を知りたい」なのかで、選ぶ型は変わります。

もう1つ、問いを書き出すときには「答えが何の形で返ってくれば意思決定できるか」まで決めておくことをお勧めします。1つの数字でよいのか、分布が要るのか、時間の推移が要るのか。ここが決まっていないと、モデルができた後で「その数字では判断できない」と言われ、作り直しになります。分布が要るのに平均しか出せない型を選んでしまう、時間の推移が要るのに静的な型を選んでしまう、という取り違えは、この一手間で防げます。

なお、問いによってはシミュレーションを作る必要がないこともあります。単純な系の平均的な待ち時間を知りたいだけなら、待ち行列の公式で数分あれば概算が出ます。制約を満たす割り当てを1つ決めたいだけなら、第1章で述べたとおり最適化の仕事です。型を選ぶ前に、そもそもこの問いにシミュレーションが要るのかを一度問い直すほうが、結果的に早く終わります。

粒度の選択は、型の選択より重い設計判断になる

型が決まったあと、最も大きな判断は粒度です。どこまで細かく作るか、逆に言えば何を捨てるか。この判断がモデルの成否をほぼ決めます。そして、粒度に関する誤解は根深いものがあります。細かく作れば作るほど現実に近づき、正確な答えが出るはずだ、という誤解です。

実際には逆のことが頻繁に起きます。粒度を1段上げると、必要なデータの種類が増えます。工程を10個の塊で表していたモデルを、設備1台ずつ200台に分解すれば、200台分の処理時間・故障間隔・修理時間・段取り時間が必要になります。そのデータが社内に揃っていることは稀で、多くは誰かの記憶や勘で埋めることになります。勘で埋めた数字が200個入ったモデルは、10個の塊で作ったモデルより現実から遠いことがあります。細かさが精度を保証するのは、細かさに見合うデータがあるときだけです。

検証の負荷も同じように増えます。第10章で扱いますが、モデルを信じるためには、出てきた数字が実績と合っているかを確かめる手続きが要ります。構成要素が10個なら、どこがずれているかを1つずつ突き合わせられます。200個になると、全体の数字が実績と合わないとき、どこが原因かを特定する作業そのものが大仕事になります。合わない原因を探しているうちに納期が来て、「だいたい合っているのでよしとする」という決着になりがちです。

計算時間も跳ね上がります。粒度が上がるほど1回の実行が重くなり、比較したい案の数だけ、さらに統計的な精度を出すための反復回数だけ掛け算になります。ここは軽視されがちですが、実務では致命的です。1回の実行に10分かかるモデルで、10案を比べ、各案100回まわせば、単純計算で1万分、およそ7日かかります。案を1つ追加するたびに1日近く待つことになれば、検討そのものが止まります。

粒度の決め方は、問いから逆算するのが原則です。「この粒度を1段下げたとき、答えは変わるか」と自問し、変わらないなら下げます。ボトルネック工程が2つ目にあると分かっている系で、5つ目以降の工程を1つの塊にまとめても、能力の答えは変わりません。であれば、まとめます。逆に、答えが変わる箇所は細かく作る必要があります。判断の基準は現実への忠実さではなく、答えへの影響です。

実務では、次の3段階で考えると整理しやすくなります。

粒度の段階 何を表現するか 必要なデータ 向く場面
粗い
(構造だけ)
主要な工程や部門を数個の塊で表す。個体差は持たない 全体の処理量、代表的な所要時間、大まかな不良率 方向性の判断、案の足切り、投資するかどうかの初期検討
中くらい
(資源単位)
設備や窓口を単位として表す。待ちと奪い合いが表現できる 資源ごとの処理時間分布、稼働パターン、到着の分布 台数や人員の決定、レイアウト変更、シフト設計
細かい
(現物単位)
個々の設備・搬送機・作業者の挙動まで表す 個体ごとの実績データ、制御ロジック、実運用の例外ルール 制御方式の検証、既存設備の詳細な改善、現場との接続

多くのプロジェクトは、粗い段階から始めて、必要なところだけを中くらいに上げていくのが安全です。最初から細かく作ると、動くまでに数か月かかり、その間に検討の前提が変わってしまいます。細かい段階が最初から必要になるのは、第15章で扱うデジタルツインのように、現場の設備と接続して継続的に使うことが決まっている場合に限られます。

型は排他ではなく、実務では組み合わせて使うことが多い

ここまで4つの型を並べてきましたが、1つを選んだら他は使えない、というものではありません。むしろ実務では組み合わせが普通です。組み合わせ方には、大きく分けて入れ子にする形と、並べて接続する形があります。

入れ子にする形の代表が、モンテカルロ法と離散イベントシミュレーションの組み合わせです。外側で需要の水準・単価・故障率といった不確実な前提を乱数で振り、そのシナリオ1つひとつを内側の離散イベントモデルで流します。結果として、「この設備構成なら、需要がどう振れても95%の確率で目標を満たせる」といった形の答えが得られます。設備投資の判断では、平均的なシナリオでの成立よりも、悪いシナリオでも耐えられるかどうかが問われることが多いため、この組み合わせは相性が良いものです。

並べて接続する形の例としては、システムダイナミクスとモンテカルロ法の組み合わせがあります。人員の育成やブランドの毀損といった、遅れを伴う構造をシステムダイナミクスで描いたうえで、その中の不確かなパラメータ、たとえば離職率や需要成長率を確率的に振ります。1本の曲線ではなく、曲線の束として結果が出てくるので、「この施策は平均的には効くが、離職率が想定より高いと逆効果になる」といった条件つきの結論が得られます。

離散イベントシミュレーションとエージェントベースモデリングの組み合わせも実務でよく使われます。工場や店舗の設備・窓口の側は資源として離散イベントで表し、そこを利用する人の側は行動ルールを持った主体として表す、という分担です。待ち時間が長いと諦めて帰る、混んでいる列を避けて別の列に移る、といった振る舞いは、資源の側だけを見ていては表現できません。人の判断が待ちの結果を変え、変わった待ちがまた人の判断を変える、という往復が本題になる場面では、この組み合わせが要ります。

組み合わせる際に注意すべきは、組み合わせた分だけ検証の対象が増えるという点です。2つの型をつないだモデルは、それぞれの型が正しく動いていることに加えて、つなぎ目の受け渡しが正しいことも確かめなければなりません。外側から内側へ渡す前提の単位が揃っているか、内側の結果を集計するときに取りこぼしがないか。ここは地味ですが、間違えると全体が静かにずれます。組み合わせは目的ではなく手段なので、単一の型で答えられるならそのほうが良い、という原則は保っておくべきです。

外側の枠に「モンテカルロ法で需要と単価のシナリオを多数生成」

よくある選び間違いは、3つの形に集約される

型の選び間違いには、繰り返し現れる型があります。3つ挙げます。

1つ目は、待ちや資源の奪い合いが本題であるにもかかわらず、集計的なモデルで済ませてしまう間違いです。表計算で「1日の処理能力は1時間あたり60件、稼働8時間だから480件」と計算するのがその典型です。この計算は、仕事が均等に到着し、設備が止まらないことを前提にしています。実際には到着は偏り、設備は止まり、その結果として待ちが生まれます。第5章で詳しく扱いますが、待ち時間は稼働率が上がると比例ではなく急激に伸びます。実測でも、単純な系で稼働率が0.5のときに平均待ち時間が1.00だったものが、0.9では9.00、0.95では19.00になりました。同じ「能力の8割で回す」という言葉でも、平均だけを見た計算と、待ちを表現したモデルとでは、結論が桁で変わります。

2つ目は、個体差が結果を決めていないのに、エージェントベースモデリングで作り込んでしまう間違いです。1人ずつ属性を持たせて行動させるモデルは、作っていて手応えがあり、動かすと現実らしく見えます。しかし、全員が同じルールで動き、相互作用もないのであれば、その結果は人数を掛けた集計モデルと一致します。一致するなら、データ収集も検証も計算時間も軽い集計モデルで足ります。判断の基準は、「隣の主体が何をしたかが、自分の行動を変えるか」です。変えないなら、個体を持つ理由はありません。

3つ目は、時間の遅れが本題であるのに、静的な計算で済ませてしまう間違いです。増員すれば生産量が増える、という関係を1つの掛け算で表してしまうと、採用してから戦力になるまでの数か月、その間に既存メンバーが教育に時間を取られて一時的に生産量が落ちること、そして落ちた分を取り返そうとしてさらに採用を増やすこと、という一連の動きが消えます。実測した例では、需要が20%増えただけの入力に対して、発注量の増幅率は調整の速さによって1.08倍から1.83倍まで変わりました。同じ需要変動でも、どれだけ急いで調整するかで結果が倍近く違うということです。この差は、時間を持たない計算からは絶対に出てきません。

3つに共通しているのは、「間違った型でも数字は出る」という点です。出た数字はもっともらしく、桁も常識的な範囲に収まります。だからこそ気づきにくい。防ぐには、モデルを作り始める前に「この問いの答えを左右しているのは何か」を1行で書いておくことです。待ちなのか、個体差なのか、遅れなのか。書いてあれば、それを表現できない型を選んだ時点で気づけます。

型が違えば、読み取れるアウトプットも違う

最後に、それぞれの型から何が返ってくるのかを整理します。型を選ぶということは、実質的にアウトプットの形を選ぶことでもあります。

モンテカルロ法から返ってくるのは、結果の分布です。平均だけでなく、中央値、上位・下位の分位点、目標を達成する確率といった形で読み取れます。実測した工期の例では、各作業の最頻値を単純に足すと108日でしたが、乱数を振って積み上げた平均は132.6日、8割の確率で収まる線は146.7日でした。そして108日以内に終わる確率は5.4%でした。最頻値の合計という一見もっともらしい見積もりが、実は達成できる確率が5%あまりしかない水準だった、という事実が分布の形で見えます。返ってくるのが分布であるため、「どこまで悪くなりうるか」「この線を守れる確率はいくらか」という問いに直接答えられます。一方で、途中で何が起きているかは分かりません。

離散イベントシミュレーションから返ってくるのは、時間の経過を伴う運用の実態です。平均待ち時間や処理量といった集計値だけでなく、待ち時間の分布、資源ごとの稼働率、待ち行列の長さの推移、どの工程で滞留したかといった内訳が読み取れます。実測した窓口の例では、窓口を3つから6つへ増やしたときの平均待ち時間が1.02分から0.03分へ、待ち時間の上位5%の水準が1.37分から0.07分へ、待ちきれずに離脱する割合が10.60%から0.34%へと変化しました。平均だけを見ると小さな差に見えますが、離脱率という別の指標では大きな差になります。複数の指標を同時に取り出せることが、この型の強みです。

エージェントベースモデリングから返ってくるのは、全体の挙動と、そこに至る個々の動きの両方です。普及率や離反率といった集計値が出るのは他の型と同じですが、加えて「誰が最初に動いたか」「どの経路で伝わったか」「どこで止まったか」が追えます。実測した普及の例では、初期の採用者を全条件で2%に固定し、個々人が採用に踏み切るしきい値だけを動かしたところ、しきい値0.24までは20回の試行すべてが100%普及したのに対し、0.26では7回だけが広がって最終普及率が35.6%にとどまり、0.30では20回とも広がらず最終普及率は4.0%でした。しきい値をわずか数ポイント動かしただけで、結果が「全体に行き渡る」と「初期の採用者からほとんど動かない」に割れます。この不連続な切り替わりは、集計的なモデルでは平均されて見えなくなります。

システムダイナミクスから返ってくるのは、時間に沿った量の推移と、その形です。行き過ぎて戻る挙動、遅れて効いてくる影響、振動して収束するまでの期間といった、構造に由来する癖が読み取れます。実測した発注の例では、需要が第5週に20%増えたあと、在庫が目標水準の1%以内に戻るまでの週数が、調整の速さによって第10週から第35週まで変わりました。ここで得られるのは「何個」という精密な数値ではなく、「この構造だと行き過ぎる」「戻るのに数か月かかる」という定性的な形の理解です。政策の方向を決める段階では、この形の理解のほうが役に立つ場面が多くあります。

アウトプットの違いを裏返して言えば、意思決定の場で求められる答えの形から、型を逆算できるということです。稟議で「最悪ケースでも成立すること」を示す必要があるならモンテカルロ法、「増設後の待ち時間が基準内に収まること」を示すなら離散イベントシミュレーション、「施策が数年かけてどう効くか」を示すならシステムダイナミクス、というように対応します。作りたい型ではなく、示すべき答えから決めるのが順序です。

型を選ぶ手順を、5つの問いにまとめる

この章の内容を、実際に手を動かす順序に落とし込みます。次の5つを順に答えれば、型と粒度の大枠は決まります。

  1. 何を知りたいのかを1文で書く。答えの単位(分・件・円・確率)まで含めて書く
  2. その答えを左右しているのは何かを1行で書く。待ちか、個体差か、遅れか、入力の振れか
  3. 時間を進める必要があるかを決める。順番や滞留が結果に効かないなら、進めない
  4. 個体を1つずつ数える必要があるかを決める。全員が同じ動きなら、数えない
  5. その答えを出すのに必要な最も粗い粒度を決める。1段下げても答えが変わらないなら下げる

この5つに答えた結果、複数の型が候補に残ることがあります。その場合は、軽いほうから作るのが原則です。粗いモデルを先に作れば、数日で最初の数字が出ます。その数字を見て「この粒度では判断できない」と分かったときに初めて、足りない部分だけを細かくします。逆の順序、つまり細かく作ってから削る進め方は、ほぼうまくいきません。作り込んだ部分を捨てる判断は心理的に難しく、結局そのまま残ってしまうためです。

また、5つの問いに答える作業は、モデルを作る側だけでやってはいけない性質のものです。とくに1つ目と2つ目は、意思決定をする人と一緒に決める必要があります。答えの単位が合意できていないモデルは、完成した瞬間に「知りたかったのはそれではない」と言われる可能性を抱え続けます。第16章で導入の進め方を扱いますが、この合意はプロジェクトの最初の1週間で取るべきものです。

次章から、それぞれの型を個別に見ていきます。まずは最も軽く、最も応用範囲の広いモンテカルロ法から始めます。時間を進めないという制約を持つ代わりに、不確実性そのものを扱う道具として、他の型の中にも部品として組み込まれていく型です。

この章を深めたい方への参考書籍

シミュレーション(白鳥則郎 監修、佐藤文明・齊藤稔・石原進・渡邊尚 著、共立出版)
モデル化から乱数生成、待ち行列モデル、微分方程式、セルオートマトン、マルチエージェントまでを一続きで扱う教科書です。本コラムで型ごとに分けて説明した内容を、体系立てて読み直せます。

シミュレーション辞典(日本シミュレーション学会 編、コロナ社)
学会が編んだ辞典です。手法名や用語の定義を確かめたいとき、分野をまたいで一度に引ける参照先になります。

第3章 モンテカルロ法、不確実性を数え上げて意思決定に渡す

第2章で並べた4つの型のうち、モンテカルロ法はもっとも単純な位置にあります。時計を進めることをせず、資源の取り合いも表現せず、個体どうしの相互作用も扱いません。やることは、入力に幅を与えて乱数で大量に試し、出てきた値を数え上げるだけです。数式としての解が求まらない量を、力ずくの試行回数で近似する。それがこの手法の全部です。

単純であることは弱さではありません。むしろ、社内で最初に効くのはたいていこの手法です。理由は導入の軽さにあります。離散イベントシミュレーションであれば、資源・待ち行列・イベントの順序といったモデル構造を組み立てる必要がありますが、モンテカルロ法に必要なのは、入力の幅と、入力から出力を計算する式だけです。式はすでに手元にあることが多い。事業計画のスプレッドシートにも、工期の積み上げ表にも、在庫の補充計算にも、入力から出力を出す計算はすでに書かれています。足りないのは、その入力が単一の数字で置かれていることだけです。

この章では、その「単一の数字」を幅に置き換えたときに何が起きるかを、実際に動かした結果の数値で確認します。結論を先に置くと、各工程の最頻値を足し上げた工期は、ほとんど達成できません。実験では、最頻値の単純合計が108日であるのに対し、実際に108日以内で終わる確率は5.4%でした。この落差がどこから来るのかを理解することが、この章の中心にあります。

点で持つのではなく、分布で持つ

事業計画にも、プロジェクトの工期にも、在庫の発注量にも、必ず数字が入っています。売上成長率12%、実装工程45日、月次需要800個。この数字は、確からしい値の代表としてそこに置かれています。問題は、代表値だけを持ち回っているうちに、それが唯一の値であるかのように扱われはじめることです。会議の資料に45という数字が印字された時点で、幅の情報は資料から消えます。

幅の情報が消えると、判断が体系的に楽観へ寄ります。工期には「思ったより早く終わる」側の余地が限られている一方、「思ったより遅れる」側には余地が大きく残っているのが普通です。要件の追加、担当者の離脱、外部連携の仕様変更といった事象は、工期を短くする方向にはほとんど働きません。つまり、幅は左右対称ではなく右に長い。代表値だけを持つと、この非対称が丸ごと落ちます。

点で持つのではなく、分布で持つとは、45という数字を「最短30日、最頻45日、最長90日」という3つの数字に置き換えることを指します。この置き換えの良いところは、現場に聞き取りできる形になっていることです。「実装は何日ですか」と聞くと答えに詰まる担当者でも、「順調にいけば何日、いつもどおりなら何日、こじれたら何日」であれば答えられることが多いように思います。幅を聞き出すために新しい計測システムを導入する必要はありません。聞き方を変えるだけで、分布を作るための材料は集まります。

そして、いったん入力を分布で持つと、出力も分布になります。「工期は132日です」ではなく「半分の確率で132日以内、8割の確率で147日以内」という形の答えが出てきます。この形の答えは、意思決定にそのまま渡せます。顧客に約束する納期をどこに置くか、社内のバッファを何日積むか、といった問いは、分布があってはじめて根拠を持って答えられるものです。

モンテカルロ法は何をしている手法か

モンテカルロ法は、解析的に解けない量を、乱数で大量に試して度数で近似する手法です。手順は3段階しかありません。第1に、不確実な入力それぞれに確率分布を割り当てる。第2に、その分布から1組の値を乱数で引き、手元の計算式に通して出力を1つ得る。第3に、これを何万回も繰り返し、集まった出力の集合を分布として読む。この繰り返しが「大量に試す」の部分です。

なぜ度数で近似できるのかは、大数の法則が保証しています。同じ分布から独立に引いた値の平均は、試行回数を増やすにつれて真の期待値に近づきます。求めたいものが平均でなく「108日以下になる確率」であっても同じことです。108日以下だったら1、そうでなければ0とする指標を作れば、その平均が確率になります。つまり、平均も確率も割合も、すべて「数え上げた結果の平均」として同じ枠組みで扱えます。

ここで押さえておきたいのは、モンテカルロ法が時間を扱っていないという点です。第4章で扱う離散イベントシミュレーションは、時計を進めながら事象を順に処理していきます。対してモンテカルロ法の1回の試行は、時間軸を持たない1回の計算にすぎません。工期の例でも、5つの工程を順番に処理しているのではなく、5つの乱数を引いて足しているだけです。工程間の待ち合わせや資源の奪い合いを表現したければ、モデルの型そのものを変える必要があります。

この単純さは制約でもあり、利点でもあります。制約としては、順序や相互作用が結果を左右する問題には向きません。利点としては、計算が独立なので並列化が容易であり、実装のバグが混入しにくく、結果の再現も乱数の種を固定するだけで済みます。後述するコードでも np.random.default_rng(42) と種を固定していますが、これは検証と妥当性確認のために欠かせない習慣です。詳しくは第10章で扱います。

不確実性の伝播、なぜ最頻値の和では終わらないのか

入力それぞれに幅があるとき、出力の幅は入力の幅の単純な足し算にはなりません。この「入力の幅が計算式を通って出力の幅に変わる過程」を確率分布の伝播と呼びます。伝播の仕方は計算式の形によって変わるため、直感がしばしば外れます。とくに外れやすいのが、和を取ったときの中心の位置です。

直感では、各工程の最頻値を足せば全体の最頻値になりそうに思えます。しかしこれは、分布が左右対称のときにしか成り立ちません。工期のように右に長い裾を持つ分布では、平均が最頻値より右にずれます。三角分布の平均は最小値・最頻値・最大値の3点の平均ですから、実装工程を「最短30日、最頻45日、最長90日」と置いた場合、その平均は55日になります。最頻値より10日も右です。これを5工程分足し上げれば、ずれも積み上がります。

後述する実験の5工程では、最頻値の合計が108日、各工程の平均を足し合わせると約132.7日になります。約25日の差が、単に「右に長い裾を持つ分布を足した」という理由だけで生まれています。誰かがサボったわけでも、見積もりが甘かったわけでもありません。最頻値を足すという計算そのものが、構造的に楽観の側へずれる作業だったということです。

もう1つ、和を取ることで起きる変化があります。個々の工程の分布は右に歪んでいますが、独立な5つを足し合わせると、合計の分布は元の分布より対称に近づきます。中心極限定理が働くためです。実験でも、平均132.6日に対して中央値131.7日と、両者はかなり近い位置に来ています。歪みは消えないものの、和を取る段階でいくらか薄まる。この性質があるため、合計工期については平均と中央値のどちらを見てもおおむね同じ結論になりますが、個別の工程を見るときには両者が離れることを意識しておく必要があります。

各工程の最頻値を足した点と

5工程の工期を10万回試す

実際に動かして確認します。架空のシステム開発案件を想定し、要件定義・設計・実装・テスト・移行の5工程それぞれに、最短・最頻・最長の3点を置きました。分布は三角分布を使います。三角分布を選ぶ理由は、必要な情報が3点だけで済むためです。正規分布であれば平均と標準偏差、ベータ分布であれば形状パラメータを聞き出す必要がありますが、現場に「標準偏差はいくつですか」と尋ねても答えは返ってきません。「最短・最頻・最長」の3点なら聞き出せます。データが乏しい段階での作業仮説として、三角分布は扱いやすい選択肢です。

import numpy as np

rng = np.random.default_rng(42)
N = 100_000

# 5工程の所要日数を三角分布(最小・最頻・最大)で置く
tasks = [
    ("要件定義", 10, 15, 30),
    ("設計",     15, 20, 35),
    ("実装",     30, 45, 90),
    ("テスト",   10, 20, 45),
    ("移行",      5,  8, 20),
]

total = np.zeros(N)
for name, lo, mode, hi in tasks:
    total += rng.triangular(lo, mode, hi, N)

print("最頻値の単純合計 :", sum(t[2] for t in tasks), "日")
print("平均             :", round(total.mean(), 1), "日")
print("P50(中央値)    :", round(np.percentile(total, 50), 1), "日")
print("P80              :", round(np.percentile(total, 80), 1), "日")
print("P95              :", round(np.percentile(total, 95), 1), "日")
print("108日以内に終わる確率 :", round((total <= 108).mean() * 100, 1), "%")

やっていることは、10万回のループを配列演算に置き換えただけです。rng.triangular が1工程あたり10万個の乱数を返し、それを5工程分足し合わせて、10万通りの合計工期を作っています。使っているのはNumPyだけで、専用のシミュレーションツールは要りません。実行結果は次のとおりです。

最頻値の単純合計 : 108 日
平均             : 132.6 日
P50(中央値)    : 131.7 日
P80              : 146.7 日
P95              : 160.8 日
108日以内に終わる確率 : 5.4 %

最頻値を足し上げた108日という数字と、実際に108日以内で終わる確率5.4%。この2つを並べると、何が起きているかがはっきりします。108日という計画は、5工程すべてがほぼ最頻値どおりに進むことを前提にしています。1工程が最頻値どおりに進む確率がそれなりにあっても、5工程が同時にそうなる確率は小さい。そして遅れた分は他工程で取り返せません。工期は足し算なので、遅れはそのまま合計に積み上がります。結果として、108日で終わるのは約18回に1回という頻度になります。

この数字は、計画の妥当性を議論するときに使えます。108日の計画を提示された側が「そのスケジュールが守られる確率はどのくらいですか」と問い、5.4%という答えが返ってくれば、議論は精神論から離れます。守れないことを責めるのではなく、どの線なら守れるのかを決める話に移せます。

指標読み方
最頻値の単純合計108日各工程が最頻値どおりに進んだ場合の工期。計画表に載りやすい数字
平均132.6日何度も繰り返した場合の平均的な工期。右の裾に引かれて中央値より右
P50(中央値)131.7日五分五分で達成できる線。社内で共有する見込みの標準
P80146.7日5回に4回は達成できる線。顧客に伝える納期の候補
P95160.8日20回に19回は達成できる線。違約金や機会損失が重い場合の候補
108日以内に終わる確率5.4%最頻値の合計で立てた計画が守られる頻度

意思決定に渡すときの線の引き方

分布が出てきても、意思決定は最後には1つの数字を要求します。納期は1つ、予算は1つ、発注量は1つです。分布から1つの数字を取り出すために使うのが分位点です。P50は「五分五分の線」、P80は「5回に4回は収まる線」、P95は「20回に19回は収まる線」を指します。どの分位点を取るかは、統計の問題ではなく、損失の非対称性をどう見るかという経営の問題です。

工期の例で考えます。P50の131.7日を顧客に伝えた場合、約半分の案件で遅延の説明をすることになります。P80の146.7日を伝えれば、遅延の頻度は5回に1回に下がります。その代わり、提案時点で15日長い工期を提示することになり、競合との比較で不利になるかもしれません。つまり、分位点の選択は「遅れて謝る損失」と「長い見積もりで失注する損失」の大小を比べる作業です。この比較は数字で答えが出るものではなく、事業としてどちらが重いかの判断になります。

在庫でも同じ構造が現れます。品切れによる機会損失と信用の毀損が、余剰在庫の保管費と廃棄損より大きい商材であれば、需要分布の右側の分位点を取って安全在庫を積みます。逆に、賞味期限が短く廃棄損が重い商材であれば、右に寄せすぎることが損になります。同じ会社の中でも商材ごとに取るべき分位点は違うはずで、それを一律の「安全在庫は2週間分」といった規則で運用していると、どちらの商材でも損をします。

予算のバッファも同様です。P50を予算として承認し、超過分を都度の稟議で処理する運用と、P80を予算として承認し、余った分を期末に戻す運用とでは、組織の動き方が変わります。前者は超過が常態化して稟議が形骸化しやすく、後者は予算が甘くなりやすい。どちらを選ぶにせよ、「この予算は分布のどこに引いた線か」を明示しておくことが、後の議論を助けます。

実務上ありがちな失敗は、分位点を選ぶ議論をしないまま、平均だけを渡してしまうことです。平均は「何度も繰り返したときの平均的な結果」であって、「達成できる確率が高い線」ではありません。今回の実験でも、平均132.6日を達成できる確率は五分五分に近い水準にとどまります。平均を約束の線として使うと、約半分の案件で約束が守られないことになります。

応用の広がり、数え上げが効く場面

この手法が使えるのは工期だけではありません。「不確実な入力があり、入力から出力を計算する式が書けている」という条件さえ満たせば、対象を選びません。以下は、手法として一般に行われている使い方を並べたものです。

対象幅を持たせる入力分布として見る出力意思決定に渡す形
事業計画のNPV販売数量、単価、原価率、立ち上がり時期正味現在価値の分布NPVが負になる確率、下位10%での損失額
プロジェクト工期各工程の所要日数完了日の分布P80での完了日、目標日を守れる確率
在庫の安全在庫需要量、調達リードタイムリードタイム中の需要の分布目標のサービス水準に対応する分位点
保険や与信の損失事故や債務不履行の発生件数、1件あたりの損失額年間損失総額の分布99%点などの分位点、期待損失との差
設備投資の回収期間稼働率、単位あたり利益、保守費用回収年数の分布目標年数以内に回収できる確率
為替や原材料価格の感応度為替レート、原材料の市況価格営業利益の分布利益が閾値を下回る確率、必要なヘッジ量

この表のどの行にも、共通する構造があります。入力に幅があり、出力は入力の非線形な関数になっていて、意思決定は分布のどこかに線を引く形で行われる、という構造です。とくに保険や与信のように、件数と1件あたり金額という2つの不確実性が掛け合わさる場面では、出力の分布が極端に右に伸びます。件数が多い年に金額の大きい案件が重なると、損失総額は平均の何倍にもなり得るためです。こうした裾の厚い分布を平均だけで管理すると、備えが足りません。

逆に、モンテカルロ法が向かない場面もあります。入力の幅が本質的に分からない場合、たとえば前例のない新規事業で需要の分布を置く根拠がまったくない場合、幅を仮定しても出てくるのは仮定の再確認にすぎません。この場合は、分布を精緻にする作業より、幅を狭めるための小さな実測を先に行うほうが実りがあります。入力の分布をどう決めるかは第8章で扱います。

相関を無視すると幅を過小評価する

先ほどのコードには、明示していない仮定が1つ入っています。5つの工程の所要日数が互いに独立だという仮定です。工程ごとに別々の乱数を引いているのですから、要件定義が長引いた試行で実装も長引く、という関係は表現されていません。この仮定が現実と合わない場合、出力の幅は過小評価されます。

なぜ過小評価になるのかは、和の分散を考えると分かります。独立な2つの量を足したとき、合計の分散は各分散の和になります。しかし正の相関があると、合計の分散は各分散の和に共分散の2倍が加わり、その分だけ大きくなります。直感的にいえば、独立なら「片方が上振れしたとき、もう片方が下振れして打ち消す」ことが起きますが、正の相関があるとその打ち消しが起きにくく、上振れどうし・下振れどうしが重なります。結果として裾が伸びます。

正の相関が生じる場面は珍しくありません。工期でいえば、要件定義が曖昧なまま進んだ案件は、実装でもテストでも手戻りが増えます。遅れの原因が工程ごとに独立ではなく、案件そのものの性質という共通の原因から来ているためです。コストでいえば、原材料費と輸送費は燃料価格という共通の要因を持つため、同時に上がりやすい。事業計画でいえば、景気後退局面では販売数量が落ちると同時に単価も下がることがあります。

対処の方向は2つあります。1つは、相関を明示的にモデルに入れることです。多変量正規分布から相関を持つ乱数を生成して各入力の分布に変換する方法や、共通の要因を別の乱数として導入し、各入力をその要因の関数として書く方法があります。後者は、原因が特定できている場合には理解しやすい書き方です。もう1つは、相関を入れない代わりに、独立仮定のもとで得た幅は下限であると明記して運用することです。精緻にできない場合でも、どちら側に誤っているかが分かっていれば、判断を誤りにくくなります。

やってはいけないのは、独立と仮定したことを記録せずに結果だけを渡すことです。受け取った側は、その幅が現実の幅だと理解します。相関を無視した結果は、実際より狭い幅を、根拠のある数字の顔をして提示することになります。

何回まわし、どこの精度を上げ、何を書き残すか

試行回数を増やせば推定は締まりますが、締まり方は緩やかです。ここで、以降の章でも繰り返し出てくる2つの言葉を確認しておきます。標準偏差は、ばらつきの大きさを表す数値です。平均からどれくらい離れた値が出やすいかを1つの数字にまとめたもので、大きいほど散らばっているという意味になります。分散はその標準偏差を2乗したもので、足し合わせの計算がしやすいため、途中の計算で使われます。第5章と第8章では、この標準偏差を平均で割った変動係数という指標も出てきます。ばらつきの大きさを、平均に対する比率で表したものです。平均10分の作業が前後3分ばらつくのと、平均100分の作業が前後3分ばらつくのでは、同じ3分でも意味が違います。その違いを吸収するための指標だと考えてください。

この言葉を使うと、標本平均の標準誤差は \( \sigma / \sqrt{N} \) で表されるとおり、試行回数 \( N \) の平方根でしか改善しません。精度を10倍にしたければ試行回数は100倍必要です。今回のコードで10万回まわしているのは、分位点まで安定して読みたいためです。平均だけであれば数千回で十分なことも多い一方、P95のように裾に近い量は、その領域に落ちる試行の数が少ないぶん、より多くの回数を要します。何回まわせば信じてよいかという問いは第9章で改めて扱います。ただし、試行回数を増やすことに費やす労力には限界効用があります。10万回を100万回にしても、変わるのは推定値の小数点以下です。それに対して、入力の分布の置き方を1つ見直せば、結果は数日から数十日の単位で動きます。労力を注ぐ先は、計算の精度ではなく入力の精度であることがほとんどです。

どの入力を見直すべきかを決めるために使うのが感度分析です。考え方は単純で、入力を1つずつその幅の下端と上端に振り、他を固定して出力がどれだけ動くかを測ります。動き幅の大きい順に横棒を並べると、上ほど棒が長い図になります。これがトルネード図と呼ばれる表現です。棒が長い入力ほど、出力の幅への寄与が大きいことを意味します。

この図から読み取るべきは、「どの入力の見積もりを精緻にすれば、出力の不確実性がいちばん減るか」です。今回の5工程でいえば、最長90日という広い幅を持つ実装工程が最上位に来ることが見込まれます。移行工程の幅を丁寧に詰めても、合計への影響は小さい。追加の実測やヒアリングは、棒の長い入力に集中させるべきです。逆にいえば、棒の短い入力にはざっくりした3点で十分だという判断もできます。

トルネード図の考え方を示す概念図

最後に限界を確認しておきます。モンテカルロ法の出力は、入力の分布という人が置いた仮定を超えません。最長90日と置けば、90日を超える実装は1度も現れません。分布の形を三角分布にすると決めた時点で、両端を超える値は出ないという性質が入ります。出てくる分布は現実の分布ではなく、置いた仮定の帰結です。10万回という試行回数の多さは、この事実を変えません。むしろ、桁数の多い数字が並ぶことで、仮定に基づいた結果が計測値のように見えてしまう危険があります。

したがって、運用上もっとも重要なのは、どの前提を置いたかを必ず書き残すことです。各工程の3点をどこから得たのか、誰がどの案件を思い浮かべて答えたのか、独立を仮定したのか相関を入れたのか、分布の形として何を選び、なぜそれを選んだのか。これらを結果の数字と同じ場所に残しておくことで、結果が外れたときに何を直せばよいかが分かります。前提の記録がなければ、外れた原因が入力の置き方にあるのか計算にあるのかを切り分けられません。

書き残す先は、コードのコメントでも、計算シートの脇の欄でも構いません。ただし、結果を報告する資料に「この工期はP80の値であり、各工程の3点見積もりは何月時点の担当者ヒアリングによる」という一文があるかどうかで、その数字が持つ重みは変わります。分布を出すところまでが分析で、前提を添えて渡すところまでが仕事です。次章では、この章では扱わなかった時間と資源の話に進み、待ち行列を持つ系をどう動かすかを見ていきます。

この章を深めたい方への参考書籍

モンテカルロ法ハンドブック(Dirk P. Kroese・Thomas Taimre・Zdravko I. Botev 著、伏見正則・逆瀬川浩孝 監訳、朝倉書店)
モンテカルロ法を体系的にまとめた一冊です。本章で扱った試行と推定の背景を、数理の側から確かめたいときに向きます。

ゼロからできるMCMC(花田政範・松浦壮、講談社)
乱数を使って分布を扱う考え方を、手を動かしながら学べる本です。本章のモンテカルロ法から一歩進んだ計算手法に触れたい方に向きます。

第4章 離散イベントシミュレーションの仕組み、時計とイベントと資源

ここから、実務でもっとも使われているシミュレーションの型を分解していきます。窓口の増減、ラインの編成、倉庫のレイアウト、外来の予約枠。こうした「人やモノが並んで、限られた設備を奪い合う」場面の検討には、ほぼこの型が使われます。第2章で名前だけ挙げた離散イベントシミュレーションです。

この章では、その内部で何が起きているのかを、できるだけ細かく開いて見せます。時計がどう進むのか、何を状態として持っているのか、どこで乱数が引かれるのか。中身を知らないままツールを操作すると、出てきた数字が信じられるものなのか判断できません。逆に、仕組みが分かっていれば、モデルが変な値を返したときに「どこを疑えばよいか」が見当がつきます。ツールの操作方法より、仕組みの理解のほうが長く役に立ちます。

後半では、実際に動かした2本のコードを使って、理論値との突き合わせと、窓口数を変えたときの比較を見ます。とくに2本目は、「窓口を1つ増やすかどうか」という現実の意思決定が、平均待ち時間よりも別の指標に強く効いていることを示しています。

なぜ「離散イベント」と呼ぶのか、時計が飛ぶという発想

離散イベントという名前は、システムの状態が変わるタイミングの性質から来ています。窓口業務を思い浮かべてみます。客が1人来店した瞬間に、待っている人数が1人増えます。窓口の担当者が対応を終えた瞬間に、窓口が1つ空きます。機械が壊れた瞬間に、そのラインの処理能力が落ちます。いずれも、変化はその瞬間にだけ起こります。

そして、その瞬間と瞬間のあいだは、何も変わりません。10時00分に客が来て、10時05分に次の客が来るとすると、そのあいだの5分間、待っている人数も、窓口の状態も、まったく動きません。連続的にじわじわ変化しているのではなく、飛び飛びの時点で階段状に切り替わっているだけです。この「飛び飛び」が離散、変化を起こす出来事が事象、英語でいうイベントです。

ここから、計算のうえで大きな利点が出てきます。状態が変わらない区間を細かく刻んで計算する意味はありません。だから、時間を等間隔に刻む必要はなく、次に何かが起きる時刻まで時計を一気に飛ばしてよいことになります。1分刻みで1日分を計算すると480ステップ必要ですが、その日に起きた出来事が到着100件と処理終了100件だけなら、200ステップで済みます。しかも1分刻みでは、1分未満の出来事の順序が正しく表現できません。飛ばすほうが速く、かつ正確です。

この対比は、第7章で扱うシステムダイナミクスと比べるとはっきりします。あちらは時間を一定の刻み幅で進め、毎ステップで微分方程式を数値的に解いていきます。連続的に変化する量を扱うので、刻まないと表現できないからです。離散イベントは逆で、変化が点でしか起きないと割り切ることで、刻まずに済ませています。同じ「時間を進めるシミュレーション」でも、時計の進め方の思想が正反対です。

もうひとつ、実務上の意味があります。時計が飛ぶということは、シミュレーション内の1日は、計算機の上では一瞬で終わるということです。だからこそ、窓口3人と4人と5人を、それぞれ200日分ずつ試す、といった比較が現実的な時間で回せます。試行の安さがこの手法の武器であり、その安さは時計の進め方から来ています。

時間軸上での「等間隔刻み」と「次の事象まで飛ばす」の違いを対比した概念図

3つの構成要素、エンティティと資源とキュー

離散イベントのモデルは、要素を3種類に分けて考えると、業務のどんな場面でも同じ形に落とせます。流れるもの、奪い合われるもの、待つ場所の3つです。

1つ目はエンティティです。システムの中を通り抜けていくものを指します。窓口業務なら客、工場なら部品や仕掛品、倉庫なら荷物やオーダー、事務処理なら伝票や申請書、病院なら患者です。エンティティは属性を持てます。客なら「用件の種類」、部品なら「品番」「加工履歴」、荷物なら「配送先」「サイズ」。属性は分岐や処理時間の決定に使われます。たとえば用件の種類によって処理時間の分布を変える、といった具合です。

2つ目は資源です。数に限りがあり、エンティティに奪い合われるものを指します。窓口、機械、作業者、フォークリフト、トラックバース、検査台、診察室、電話回線。資源は「いくつあるか」という容量を持ち、使用中か空きかという状態を持ちます。ここが離散イベントの中心です。もし資源が無限にあれば、誰も待たず、行列も生じず、シミュレーションする意味はほとんどありません。資源制約があるからこそ、待ちが生まれ、詰まりが生まれ、順序の問題が生まれます。

3つ目はキューです。資源が空くまでエンティティが待つ場所です。キューには規律があります。先に来た順に処理する先入れ先出しが基本ですが、優先度つきの規律もあります。救急外来のトリアージ、コールセンターの優良顧客優先、工場での納期の近い順。この規律の選び方が結果を大きく変えることがあり、規律の比較そのものがシミュレーションの目的になる場合もあります。また、キューには容量の上限を設けられます。待合スペースが10席しかない、コンベアに置ける仕掛品は5個まで、といった物理的な制約です。上限に達したときにどうするか、つまり客が帰るのか、上流が止まるのかも、モデルの一部として決めておく必要があります。

この3つに加えて、エンティティが資源を使って何をするかの手順、つまりプロセスを書けば、モデルの骨格は完成します。「来店する、受付の順番を待つ、窓口を使って処理を受ける、退店する」。工場なら「投入される、加工機の空きを待つ、加工される、検査の空きを待つ、検査される、出荷される」。業種が違っても、書き方の型は同じです。この抽象化の効きの良さが、離散イベントが幅広い業務に適用できる理由です。

逆に言えば、モデルを作る作業の大半は、現場の動きをこの3要素に翻訳することです。翻訳の段階で「これは資源なのか、それとも単なる待ち時間なのか」「この人は資源として数えるべきか」といった判断が必要になります。たとえば、加工中に作業者が張り付いている必要がなければ、作業者は加工の全期間を占有する資源ではありません。ここを雑に扱うと、稼働率の数字が実態とずれます。第10章で扱う妥当性確認は、こうした翻訳が現場と合っているかを確かめる手続きでもあります。

時計の進み方を手で追う

仕組みの核心は、時計の進め方です。エンジンの中では、次に起きる予定の事象を時刻の早い順に並べたリストが管理されています。これを事象リスト、あるいは将来事象表と呼びます。実装上は優先度つきキューが使われることが多く、時刻をキーにして最小の要素を取り出せるようになっています。

エンジンがやることは、次の繰り返しだけです。第一に、将来事象表から先頭、つまり最も時刻の早い事象を取り出します。第二に、シミュレーション内の時計をその時刻まで進めます。第三に、その事象に応じて状態を更新します。第四に、その結果として新しく発生が確定した事象を、時刻を計算して将来事象表に入れます。そして第一に戻ります。表が空になるか、終了時刻に達したら止まります。

言葉だけでは分かりにくいので、小さな例を手で追ってみます。窓口が1つだけあり、客が3人来る場面です。説明のために、到着時刻と処理時間はあらかじめ決め打ちにしておきます。客Aは時刻0.0分に到着して処理に5.0分、客Bは時刻3.0分に到着して処理に4.0分、客Cは時刻4.0分に到着して処理に2.0分かかるものとします。実際のモデルではこれらは乱数で決まりますが、追跡しやすさを優先して固定します。

開始時点で、将来事象表には3人の到着だけが入っています。ここから6ステップで最後まで進みます。

ステップ時計取り出した事象状態の更新更新後の将来事象表
初期0.0(なし)窓口は空、キューは空0.0 Aの到着 / 3.0 Bの到着 / 4.0 Cの到着
10.0Aの到着窓口が空いているのでAが即座に開始(待ち0.0分)。窓口は使用中、キューは空3.0 Bの到着 / 4.0 Cの到着 / 5.0 Aの終了
23.0Bの到着窓口は使用中なのでBはキューへ。窓口は使用中、キューはB4.0 Cの到着 / 5.0 Aの終了
34.0Cの到着窓口は使用中なのでCもキューへ。窓口は使用中、キューはB、C5.0 Aの終了
45.0Aの終了Aが退出。キュー先頭のBが開始(待ち2.0分)。窓口は使用中、キューはC9.0 Bの終了
59.0Bの終了Bが退出。Cが開始(待ち5.0分)。窓口は使用中、キューは空11.0 Cの終了
611.0Cの終了Cが退出。窓口は空、キューは空。表が空になったので終了(空)

この表から読み取れることがいくつかあります。まず、時計は 0.0、3.0、4.0、5.0、9.0、11.0 と飛んでいます。11分間の出来事を6ステップで処理しました。もし0.1分刻みで回したなら110ステップ必要で、そのほとんどは何も起こらない空回りです。

次に、将来事象表の中身が入れ替わっていく様子です。ステップ1でAが窓口を取った瞬間に、「5.0分にAの終了」という新しい事象が確定して表に入りました。処理時間が決まらないと終了時刻は決められないので、開始した瞬間に初めて予約できます。同じように、ステップ4でBが開始した瞬間に「9.0分にBの終了」が入ります。事象が事象を生むという構造になっており、この連鎖がシミュレーションを前に進めています。

3つ目に、待ち時間がどう測られるかです。Aは0.0分、Bは5.0分から3.0分を引いた2.0分、Cは9.0分から4.0分を引いた5.0分待ちました。平均すると約2.33分です。到着時刻と開始時刻の差を記録するだけで、待ち時間の統計が取れます。実際のモデルでは、これを何万人分も集めて分布を見ます。

実務のモデルでは、事象の種類はもっと増えます。到着、サービス開始、サービス終了、故障の発生、修理の完了、シフトの交代、休憩の開始と終了、客の離脱、在庫の補充の到着。しかし、エンジンの動きは変わりません。表から取り出し、時計を進め、状態を変え、新しい事象を入れる。この4行の繰り返しが、どれほど複雑なモデルでも共通の骨組みです。

モデルの書き方の3つの流儀

同じエンジンを使うとしても、モデルを人間がどう記述するかには流儀があります。文献では世界観と呼ばれ、大きく3つに分けられます。歴史的にこの順で発展してきました。

1つ目は事象スケジューリング法です。事象の種類ごとに「その事象が起きたとき何をするか」を書きます。到着事象が起きたら、待ち人数を1増やし、窓口が空いていればサービスを開始し、次の到着を予約する。終了事象が起きたら、窓口を解放し、キューに人がいれば次の人を開始する。表の書き換えを直接記述する形なので、エンジンの動きがそのままコードに現れます。実行効率は高いのですが、1人の客の流れが複数の事象ハンドラに散らばってしまい、読み手が全体像をつかみにくいという難点があります。

2つ目は活動走査法です。時計を進めたあと、あらゆる活動について「今この活動を始められる条件がそろっているか」を走査し、そろっているものを実行します。条件を中心に書けるので、複数の資源が同時にそろわないと始まらないような処理を素直に表せます。組み立てのように、部品と作業者と治具が同時に必要な工程を書くときに向きます。一方で、毎回すべての条件を調べるため、モデルが大きくなると計算量がかさみます。この考え方を改良した三段階法という流儀もあります。

3つ目はプロセス指向法です。エンティティ1つの立場に立って、その一生を上から順に書きます。「到着する、窓口を要求する、取れたら処理時間だけ経過させる、窓口を返す、退出する」。人間が業務フローを説明するときの語り口と同じ順序なので、読みやすく、現場の担当者と突き合わせやすいという利点があります。将来事象表の操作は言語やライブラリの側が肩代わりし、書き手からは見えません。

現代の道具は、ほとんどがプロセス指向です。Pythonのライブラリである SimPy もこの流儀を採っています。この章のあとで示すコードを見ると、客の関数のなかに「窓口を要求する」「待つ」「処理時間だけ経過させる」が縦に並んでいるのが分かります。書き手は将来事象表を意識しませんが、内部では前節で追った動きがそのまま起きています。抽象化されているだけで、原理は同じです。

流儀記述の単位読みやすさ向く場面
事象スケジューリング法事象の種類ごとの処理低い。1つの流れが分散する実行速度を極限まで求める場合、エンジン自体の実装
活動走査法活動の開始条件中程度複数資源が同時にそろって初めて始まる工程
プロセス指向法エンティティ1個の一生高い。業務フローと同じ順序ほとんどの実務モデル。現代のツールの標準

流儀の違いは、モデルの表現力そのものを変えるわけではありません。どの流儀で書いても、同じ現象は表せます。違うのは、書きやすさと読みやすさ、そして現場との会話のしやすさです。モデルは作って終わりではなく、現場の人に見せて「この動きで合っていますか」と確認する工程が必ず入ります。そのとき説明しやすい形で書かれていることには、実務上の価値があります。

確率が入る場所

ここまでの手追いの例では、到着時刻も処理時間も決め打ちにしました。実務のモデルでは、これらはすべて確率的に決まります。決定論的に固定してしまうと、第5章で扱うように、ばらつきが待ち時間を生むという最も重要な性質が消えてしまうためです。

確率が入る場所は、ほぼ決まっています。第一に到着間隔です。次の客がいつ来るか。多くの場面では指数分布が使われますが、これは到着が互いに独立でランダムに起きるという仮定に対応します。時間帯によって到着率が変わる場合は、率そのものを時刻の関数にします。第二に処理時間です。1件あたりどれだけかかるか。対数正規分布やガンマ分布がよく使われます。用件の種類ごとに分布を分けることもあります。

第三に故障の発生と修理時間です。設備がいつ壊れ、復旧までどれだけかかるか。稼働時間の累積で決める場合と、経過時間で決める場合があります。第四に不良の発生です。検査を通るか、手戻りが発生するか。ベルヌーイ試行として扱うのが基本です。第五に客が待てる時間です。どれだけ待たされたら帰ってしまうか、電話を切ってしまうか。この後のコードでは、この待てる時間にも分布を置いています。

これらの分布をどう決めるかが、モデルの信頼性を左右します。現場のデータがあるなら、そこから推定します。データがないなら、専門家の見立てから三角分布などで仮置きし、その仮置きが結果をどれだけ動かすかを感度分析で確かめます。この作業を入力モデリングと呼び、第8章でまとめて扱います。ここで押さえておきたいのは、離散イベントのモデルにおける不確実性は、この5か所から入ってくるという構造です。結果が現場と合わないとき、疑うべき場所の候補がこれで絞れます。

もうひとつ注意点があります。同じ平均でも、ばらつきの形が違えば結果はまったく変わります。処理時間の平均が1分だとして、常にきっかり1分かかる場合と、平均1分の指数分布に従う場合とでは、待ち時間が倍近く違います。これは第8章で実測を示しますが、平均だけを合わせて分布の形を適当に選ぶと、モデルは平気で嘘をつきます。

理論値と突き合わせて、モデルが正しく組めているか確かめる

ここから実際のコードを見ます。最初の1本は、モデルの検証を目的にしたものです。待ち行列理論には、条件を絞れば平均待ち時間を式で計算できる場合があります。もっとも単純な M/M/1 と呼ばれる型がそれで、到着間隔が指数分布、処理時間も指数分布、窓口が1つという条件です。この場合、平均待ち時間は \( W_q = \rho / (\mu – \lambda) \) で求まります。\( \lambda \) は到着率、\( \mu \) はサービス率、\( \rho = \lambda / \mu \) は利用率です。

答えが分かっている問題をシミュレーションで解いて、同じ答えが出るかを確かめる。これが、モデルを組んだあとに最初にやるべきことです。もしここでずれるなら、エンジンの使い方かモデルの記述に誤りがあります。実務では答えが分からない問題を解くわけですが、だからこそ、答えの分かる問題で道具の当たりを取っておく意味があります。

import numpy as np
import simpy
from scipy import stats

LAM, MU = 0.8, 1.0          # 到着率・サービス率(人/分)
SIM_TIME, N_REP = 200_000, 10

def run(seed):
    rng = np.random.default_rng(seed)
    env = simpy.Environment()
    server = simpy.Resource(env, capacity=1)
    waits = []

    def customer(env):
        t0 = env.now
        with server.request() as req:
            yield req
            waits.append(env.now - t0)
            yield env.timeout(rng.exponential(1 / MU))

    def source(env):
        while True:
            yield env.timeout(rng.exponential(1 / LAM))
            env.process(customer(env))

    env.process(source(env))
    env.run(until=SIM_TIME)
    w = np.array(waits[1000:])          # 立ち上がり1000人を捨てる
    return w.mean()

rho = LAM / MU
wq_theory = rho / (MU - LAM)            # M/M/1 の平均待ち時間

means = np.array([run(s) for s in range(N_REP)])
# 反復が10回と少ないので、正規分布の1.96ではなく自由度9のt分布の値を使う
t = stats.t.ppf(0.975, N_REP - 1)
half = t * means.std(ddof=1) / np.sqrt(N_REP)

print("利用率 rho        :", rho)
print("理論値 Wq         :", round(wq_theory, 4), "分")
print("シミュレーション  :", round(means.mean(), 4), "分")
print("95%%信頼区間(t分布): [%.4f, %.4f]" % (means.mean() - half, means.mean() + half))
print("理論値が区間内か  :", bool(means.mean() - half <= wq_theory <= means.mean() + half))

コードの中身を、前節までの用語と対応させておきます。simpy.Resource(env, capacity=1) が資源、つまり窓口1つです。customer 関数がプロセス指向の記述そのもので、来店時刻を記録し、窓口を要求し、取れた時点で待ち時間を記録し、処理時間だけ時計を進めて退出します。server.request() で待たされているあいだ、この客は自動的にキューに入っています。キューの管理も、将来事象表の操作も、ライブラリ側が引き受けています。

source 関数が到着の生成器です。指数分布から次の到着までの間隔を引き、その時間だけ待って、新しい客のプロセスを起動する。これを永久に繰り返します。確率が入っている場所は2か所だけで、到着間隔と処理時間です。前節で挙げた5か所のうち、この単純なモデルでは2か所しか使っていません。

実行結果は次のとおりです。

利用率 rho        : 0.8
理論値 Wq         : 4.0 分
シミュレーション  : 4.0307 分
95%信頼区間(t分布): [3.9541, 4.1073]
理論値が区間内か  : True

理論値の4.0分に対して、シミュレーションの推定値は4.0307分でした。10回の独立した試行から求めた95%信頼区間は [3.9541, 4.1073] で、この区間のなかに理論値の4.0が入っています。モデルが正しく組めていると判断してよい結果です。

この区間の作り方には、一点ことわっておくことがあります。反復はわずか10回です。ばらつきの大きさを10個の値から推定している以上、その推定自体に誤差が乗るので、正規分布の1.96を掛けて区間を作ると幅が足りなくなります。コードで stats.t.ppf(0.975, N_REP - 1) を使っているのはそのためで、自由度9のt分布の値、およそ2.26を掛けています。反復が30回を下回る場面ではt分布を使うという第9章の話が、そのままここに当てはまります。1.96で作った区間はこれより狭くなり、この例では結論は変わりませんが、判定が際どいときには「入っている」と「外れている」を取り違えかねません。

ここで大事なのは、4.0307という数字が4.0とぴったり一致していないことを問題視しない点です。シミュレーションは有限回の試行から推定を行うので、必ず誤差が乗ります。見るべきは点推定の値ではなく、区間が理論値を含んでいるかです。逆に、区間が理論値を外していたら、そこには偶然では説明できない何かがあります。分布の指定を間違えている、立ち上がり期間の除去が足りない、資源の容量を取り違えている、といった具合です。

コードのなかの waits[1000:] という記述にも意味があります。シミュレーションを空の状態から始めると、最初のうちは行列がまだ育っておらず、待ち時間が本来より短く出ます。この立ち上がりの区間を捨てないと、平均が下振れします。ここでは先頭1000人分を捨てました。どれだけ捨てるべきかは第9章で扱います。

この「答えの分かる問題で確かめる」という手続きは、第10章で扱う検証の代表的な方法です。ほかにも、極端な条件を入れて挙動が理屈どおりになるかを見る方法、処理時間を固定してすべて手計算と突き合わせる方法などがあります。実務のモデルを組むときは、まず窓口1つの単純な形で理論値と合わせ、それから資源を増やし、故障を入れ、シフトを入れる、と段階的に育てていくのが安全です。一気に複雑なものを作ると、ずれたときに原因の切り分けができなくなります。

窓口を1つ増やすと何が変わるか

2本目のコードは、実務の意思決定に近い形です。ある窓口業務で、担当者を何人置くべきかを検討します。1分あたり0.9人が来店し、1件あたりの処理時間は平均3分の対数正規分布に従うものとします。営業は8時間、つまり480分です。ここに、客が待てる時間という要素を加えます。平均10分の指数分布に従い、その時間を超えて順番が回ってこなければ、客は帰ってしまいます。窓口の数を3から6まで振って、200日分ずつ試します。

import numpy as np
import simpy

ARRIVAL_RATE = 0.9          # 1分あたり0.9人が来店
SERVICE_MEAN = 3.0          # 1件あたり平均3分(対数正規)
SERVICE_SIGMA = 0.5
OPEN_MIN = 480              # 8時間営業
N_REP = 200

def run_day(n_staff, seed):
    rng = np.random.default_rng(seed)
    env = simpy.Environment()
    staff = simpy.Resource(env, capacity=n_staff)
    waits, abandoned = [], []

    def customer(env):
        t0 = env.now
        patience = rng.exponential(10.0)        # 待てる時間
        with staff.request() as req:
            res = yield req | env.timeout(patience)
            if req in res:
                waits.append(env.now - t0)
                mu = np.log(SERVICE_MEAN) - SERVICE_SIGMA ** 2 / 2
                yield env.timeout(rng.lognormal(mu, SERVICE_SIGMA))
            else:
                abandoned.append(1)

    def source(env):
        while True:
            yield env.timeout(rng.exponential(1 / ARRIVAL_RATE))
            if env.now > OPEN_MIN:
                return
            env.process(customer(env))

    env.process(source(env))
    env.run()
    n = len(waits) + len(abandoned)
    # 1日ぶんの「平均待ち」と「客ごとの待ち時間の95%点」と「離脱率」を返す
    if not waits:
        return 0.0, 0.0, len(abandoned) / n * 100
    return np.mean(waits), np.percentile(waits, 95), len(abandoned) / n * 100

print("%-4s %-14s %-20s %-10s"
      % ("窓口", "平均待ち(分)", "客の待ちの95%点(分)", "離脱率(%)"))
for c in (3, 4, 5, 6):
    res = np.array([run_day(c, 10_000 + s) for s in range(N_REP)])
    print("%-4d %-14.2f %-20.2f %-10.2f"
          % (c, res[:, 0].mean(), res[:, 1].mean(), res[:, 2].mean()))

先ほどのコードとの違いは3つあります。1つ目は capacity=n_staff で資源の容量を変えられるようにしたこと。2つ目が yield req | env.timeout(patience) という書き方です。これは「窓口が取れる」か「我慢の限界が来る」かのどちらか早いほうを待つという意味で、後者が先に来たら客は離脱します。前節で挙げた「客が待てる時間」の確率が、ここに入っています。

3つ目は、run_day が1日ぶんの結果として3つの値を返すことです。平均待ち時間、客ごとの待ち時間の95%点、離脱率の3つです。np.percentile(waits, 95) がその日に来た客の待ち時間を並べて上から5%目を取る部分で、外側ではこれを200日ぶん平均しています。ここを「日ごとの平均待ち時間を200日ぶん並べて、その95%点を取る」と書いてしまうと、意味がまったく別のものになります。前者は客の体験のばらつきを測り、後者は日の良し悪しのばらつきを測ります。分位点を計算するときは、何を並べたうえでの分位点なのかを必ず確認する必要があります。

実行結果です。

窓口   平均待ち(分)        客の待ちの95%点(分)         離脱率(%)
3    1.02           3.82                 10.60
4    0.34           1.89                 3.54
5    0.11           0.83                 1.19
6    0.03           0.14                 0.34

読み取れることを順に挙げます。

窓口数平均待ち時間客の待ちの95%点離脱率前の行からの改善幅
31.02分3.82分10.60%基準
40.34分1.89分3.54%平均0.68分/95%点1.93分/離脱率7.06ポイント
50.11分0.83分1.19%平均0.23分/95%点1.06分/離脱率2.35ポイント
60.03分0.14分0.34%平均0.08分/95%点0.69分/離脱率0.85ポイント

まず、平均待ち時間だけを見ていると、この意思決定は判断を誤ります。窓口3人のときの平均待ち時間は1.02分です。1分待つだけなら問題ないように見えます。しかし同じ条件で、来店した客の10.60%が待ちきれずに帰っています。10人に1人が用を足さずに帰る状態を「平均1分待ち」と表現するのは、実態とかけ離れています。

なぜこうなるのか。理由は、離脱した客の待ち時間が平均に入っていないからです。長く待たされた客ほど離脱しやすいので、離脱を許すモデルでは、待ち時間の平均は最後まで残った客だけの平均になります。混雑がひどいほど、待ち時間の長い客が集計から抜け落ち、平均が下振れします。混雑を測る指標として平均待ち時間だけを使うと、混雑しているときほど過小評価が強くなるという、都合の悪い性質があります。

したがって、この種の検討では離脱率を主指標に据えるべきです。離脱率は、待ちの長さを直接には示しませんが、機会損失の量そのものを表します。窓口を3人から4人に増やすと、離脱率は10.60%から3.54%へ、7.06ポイント下がります。来店が1日あたり約432人ですから、およそ30人が帰らずに済む計算になります。この30人が持ち帰ったはずの用件の価値が、増員1人分のコストに見合うかどうか。これが検討すべき比較です。

2つ目に読み取れるのは、効果が逓減していくことです。3人から4人への1人が離脱率を7.06ポイント下げるのに対し、4人から5人は2.35ポイント、5人から6人は0.85ポイントしか下げません。1人あたりの人件費は同じなのに、得られる効果は3分の1ずつに減っていきます。「増やせば増やすほど良くなる」わけではなく、どこかで割に合わなくなる点があります。その点がどこかは、この表がないと分かりません。

この逓減は、資源の使われ方から説明できます。1分あたり0.9人が来て1件3分かかるので、必要な処理能力は 0.9 × 3 = 2.7 人分です。窓口3人なら利用率は約0.9、4人なら約0.675、5人なら0.54です。利用率が0.9を超えるあたりから待ち時間は急激に伸び、下がるにつれて急激に縮みます。この非線形性が第5章の主題であり、逓減の正体でもあります。

3つ目が、この表でもっとも重要な列です。客の待ちの95%点を見てください。窓口3人のときの平均待ち時間は1.02分ですが、95%点は3.82分あります。20人に1人は3.82分待っているということで、平均の3倍以上です。平均だけを見て「1分待ち」と社内に報告すると、この3.82分は誰の目にも触れません。

そして、経営が顧客に約束するのは平均ではなく上限であることが多いはずです。「平均1分でご案内します」という約束は、実際にはあまり機能しません。約束として意味を持つのは「5分以上はお待たせしません」という形であり、それが守れているかを測るには、平均ではなく95%点のような分位点が要ります。第3章で扱った分位点の考え方が、待ち行列の文脈でも同じ役割を果たしています。

さらに重要なのは、平均と95%点とでは、窓口を増やす効果の見え方そのものが違うことです。窓口を3人から4人へ増やしたとき、平均待ち時間は1.02分から0.34分へ、0.68分だけ改善します。同じ増員で、95%点は3.82分から1.89分へ、1.93分改善します。改善の絶対量が3倍近く違います。5人から6人でも同じことが起き、平均は0.08分しか動きませんが、95%点は0.69分動きます。平均だけを根拠に「もう十分に短いので増員は不要」と判断すると、実際にはまだ長く待たされている層が残っていることを見落とします。

これはモデルの精度の問題ではなく、指標の選び方の問題です。シミュレーションは平均も分位点も最大値も同じ計算から同じコストで取り出せるので、平均だけを報告する理由はありません。第9章では、平均以外の指標を必ず見ることと、指標ごとに必要な反復回数が違うことを扱います。分位点は平均よりも推定が不安定で、同じ精度を得るには多くの反復が必要になるという性質があるためです。

窓口数を横軸にとり

何が出力として取れるか、ボトルネックの見つけ方と向く業務

ここまでは待ち時間と離脱率を見てきましたが、離散イベントのモデルから取れる情報はそれだけではありません。むしろ、平均値の一覧しか見ないのはこの手法の使い方として不十分です。取れるものを整理しておきます。

  • 待ち時間や滞在時間の分布。平均だけでなく、中央値、上位5%、最大値。悪い側がどこまで伸びるかは、サービス品質の議論に直結します
  • 時間帯別の推移。1日を通じて均されると見えない、昼のピークや締め切り前の集中が見えます
  • 資源ごとの稼働率。どの窓口、どの機械、どの作業者がどれだけ埋まっていたか
  • キューの長さの推移。時刻ごとに何人が待っていたか。最大値と、その最大値がいつ出たか
  • 仕掛品数。システム内に同時に存在しているエンティティの数。工場では在庫、事務処理では未処理件数にあたります
  • 処理件数とスループット。単位時間あたり何件さばけたか。離脱を含むモデルでは、来た数とさばけた数の差も見ます
  • ブロックや飢餓の時間。下流が詰まって送り出せなかった時間、上流から来なくて手が空いた時間

これらを組み合わせると、ボトルネックの特定ができます。見方はいくつかあります。第一に、稼働率が突出して高い資源を探します。他が0.6程度なのに1つだけ0.95なら、そこが制約になっている可能性が高いといえます。第二に、キューが伸び続ける場所を探します。時間とともに待ち行列が単調に長くなっていくなら、その工程の処理能力が到着量に追いついていません。定常状態に落ち着かず発散していく形が出たら、能力不足のサインです。

第三に、ブロックが起きている場所の1つ上流を見ます。ある工程が「作ったのに次に渡せない」で止まっているなら、原因はその工程ではなく下流にあります。稼働率だけを見ると、ブロックで止まっている工程も忙しく見えてしまうことがあるので、稼働の内訳を「実処理」「待機」「ブロック」に分けて集計しておくと判断を誤りにくくなります。

第四に、資源を1つずつ増やしてみるという直接的な方法があります。ある資源を1つ増やしたときに全体のスループットが改善するなら、そこが効いています。何を増やしても変わらないなら、制約は資源ではなく、順序や規律や搬送にあります。試行が安いというシミュレーションの性質は、こうした総当たりに向いています。

ここで注意しておきたいのは、ボトルネックは動くということです。ある工程を強化すると、制約は別の場所へ移ります。1回の分析で「ここがボトルネックだ」と決めて終わりにせず、対策を入れた状態で再度回して、次の制約がどこに現れるかまで見ておくと、投資の順序が立てられます。第12章で扱うシミュレーション最適化は、この探索を体系的に行う方法です。

ここまで整理した仕組みと出力を踏まえると、この手法がどんな業務に向くかも見えてきます。離散イベントが向くのは、個別の対象が、数に限りのある資源を順番待ちしながら通り抜けていく業務です。この条件に当てはまる場面は、業種を問わず広く存在します。

窓口業務やコールセンターは、もっとも典型的です。何人配置するか、時間帯ごとにどう配分するか、スキルベースの振り分けをどう設計するか。この章のコードがそのまま当てはまる領域です。工場の生産ラインもよく使われます。工程の順序、バッファの大きさ、段取り替えの頻度、設備が止まったときの影響範囲。物理的な制約が多く、平均だけの計算では答えが出にくいので、シミュレーションの出番になります。

倉庫のピッキングと出荷も同様です。作業者の人数、通路のレイアウト、ピッキングの順序、仕分け機の能力、トラックの到着タイミング。ここでは搬送そのものが時間を食う要素になり、動線の設計が結果を変えます。病院の外来は、患者という個別の対象が、受付、診察、検査、会計と複数の資源を順に使っていく構造で、予約枠の設計や診察室の配分の検討に使われます。

空港の保安検査は、レーン数、開ける時間帯、優先レーンの設計といった検討に向きます。到着が便のスケジュールに強く依存するため、時間帯別の到着率を入れる必要がある例でもあります。コンテナターミナルは、船の到着、クレーンの割り当て、ヤードの配置、トラックのゲート処理が絡み合う複雑な系で、大規模なモデルが組まれる分野として知られています。

逆に、向かない場面もあります。個別の対象を追う必要がなく、全体の量的な流れだけを見たいなら、第7章のシステムダイナミクスのほうが軽く済みます。対象どうしが影響し合い、相手の行動を見て自分の行動を変えるような構造なら、第6章のエージェントベースが適します。資源の取り合いがなく、単に不確実な量を足し合わせるだけなら、第3章のモンテカルロで足ります。「並んで待つ」という構造があるかどうかが、離散イベントを選ぶかどうかの目安になります。

この章では、仕組みと基本的な使い方を見てきました。次の第5章では、なぜ待ちが生まれるのか、なぜ稼働率が高いと待ち時間が急に伸びるのかを、待ち行列の理屈から扱います。この章で「窓口3人だと利用率が0.9になり、そこから待ちが急に増える」と述べた部分の裏づけにあたる内容です。

この章を深めたい方への参考書籍

形式的モデル化(平石邦彦、森北出版)
離散事象システムのモデル化と解析を正面から扱う本です。本章で概略を示した仕組みを、形式的な定義から積み上げて理解したい方に向きます。

第5章 待ち行列の理屈、稼働率とばらつきが待ち時間を決める

第4章では、時計と事象リストと資源で待ち行列を動かす仕組みを見ました。ただし、待ちに関わる性質のうち相当な部分は、シミュレーションを一度も回さずに紙の上で分かります。到着とサービスの平均、窓口の数、そしてばらつきの大きさ。この4つが分かれば、平均待ち時間の桁は手計算で押さえられます。本章はその「解ける範囲」を扱います。

手計算で分かることを手計算で済ませる理由は2つあります。1つは速いことです。会議の場で数字を出せるかどうかは、意思決定の速度に直結します。もう1つは、後で作るシミュレーションモデルの検算に使えることです。単純な条件に落としたときに理論値と一致しないモデルは、どこかが間違っています。第10章で扱う検証の実務では、この「手計算との突き合わせ」が最初の関門になります。

そしてもう1つ、本章には経営側に直接効く主題があります。設備や人員の稼働率を上げることは、一般に良いこととされています。ところが待ち行列の理屈では、稼働率を上げると待ち時間は比例ではなく加速度的に増えます。0.50から0.90へ稼働率を上げると、平均待ち時間は9倍になります。この非線形性を数値で確認することが、本章の中心です。

ケンドールの記法、待ち行列を共通の語彙で指す

待ち行列理論は、到着してくる何かが、限られた窓口の前で列を作る状況を数学的に扱う分野です。対象は銀行の窓口でも、工場の機械の前に並ぶ仕掛品でも、コールセンターに入る電話でも、サーバに届くリクエストでも構いません。並ぶものと処理するものがあり、処理能力に限りがあれば、同じ理屈が当てはまります。

この分野で条件を指定するときに使われるのが、ケンドールの記法です。もともとは3つの記号をスラッシュで区切る形で、順に「到着の分布」「サービス時間の分布」「窓口の数」を表します。たとえば M/M/1 は、到着がポアソン過程、サービス時間が指数分布、窓口が1つの系を指します。ポアソン過程というのは、到着が互いに影響し合わずばらばらに起きる状況のことで、次の到着までの間隔が指数分布に従う、と言い換えられます。必要に応じて4番目以降に、系に入れる最大人数や、待っている人を呼ぶ順序を書き足します。

記号のうち M はマルコフ的、つまり指数分布を意味します。指数分布は「今までどれだけ待ったかが、これから先の待ちに影響しない」という性質を持つ分布で、数学的に扱いやすいため理論の基準になっています。D は一定、つまりばらつきがゼロの決まった時間です。G は一般、つまり分布の形を特定しないという意味です。G が入った瞬間に厳密解は難しくなり、近似式か、シミュレーションの出番になります。

記法到着サービス時間窓口解けるか
M/M/1ポアソン指数分布1厳密解あり。最も基本的な形
M/M/cポアソン指数分布c厳密解あり。アーランC式を使う
M/D/1ポアソン一定1厳密解あり。待ちは M/M/1 の半分
M/G/1ポアソン任意1平均待ちは解ける。分布の平均と分散が要る
G/G/c任意任意c厳密解なし。近似式かシミュレーション

この記法を覚える実務上の意味は、前提の共有にあります。「平均待ち時間はどれくらいですか」という問いに答えるとき、答えは前提によって何倍も変わります。到着が完全に均等なのか、ばらばらに来るのか。サービス時間が一定なのか、大きくばらつくのか。窓口を1つにまとめるのか、分けるのか。ケンドールの記法は、この前提を短い記号列で明示する道具です。前提を書かずに数字だけを出すと、後で議論が噛み合わなくなります。

もう1つ押さえておくべき記号があります。M/D/1 の待ちが M/M/1 の半分になる、という点です。到着の仕方は同じで、サービス時間のばらつきをゼロにしただけで、待ち時間が半減します。設備を1台も増やさずに待ちを半分にできる可能性がここに示されています。この「ばらつきが待ちを作る」という論点は、本章の後半で改めて扱います。

リトルの法則、3つの量のうち2つが分かれば残りが出る

リトルの法則は、待ち行列に関する式の中で最も応用範囲が広いものです。式そのものは \( L = \lambda W \) の一行で終わります。\( L \) は系の中にいる平均の個数、\( \lambda \) は単位時間あたりに入ってくる平均の個数、\( W \) は1つが系に滞在する平均の時間です。待っている人の数だけを見たいときは、待ち行列に限定して \( L_q = \lambda W_q \) と書きます。

この法則の驚くべき点は、前提が非常に緩いことです。到着の分布を問いません。サービス時間の分布も問いません。窓口が何台あっても、待っている人を呼ぶ順序がどうであっても、途中で列の内部が入れ替わっても成り立ちます。必要なのは、長い目で見て系が安定していること、つまり入ってくる量と出ていく量が釣り合っていることだけです。分布の形に一切依存しないという意味で、待ち行列の式の中では例外的な立ち位置にあります。

製造や物流の現場では、同じ式が別の言葉で呼ばれています。仕掛品の数が \( L \)、スループットが \( \lambda \)、リードタイムが \( W \) に対応します。つまり「仕掛品数 = スループット × リードタイム」です。この3つのうち2つを測れば、残りの1つは計算で出ます。現場でリードタイムを直接測るのは手間がかかりますが、仕掛品の数を数えるのは目視でもできますし、スループットは日々の生産実績から取れます。測りにくい量を、測りやすい量から導けるところに実務的な価値があります。

数値で確認します。窓口の前に平均で12人が並んでいて、1分あたり3人が到着している売り場があるとします。\( L_q = 12 \)、\( \lambda = 3 \) ですから、\( W_q = 12 / 3 = 4 \) 分です。並んでいる人数を数えるだけで、平均の待ち時間が4分だと分かります。逆に「待ち時間を2分以内に抑えたい」という目標があれば、列の長さを6人以内に保てばよい、と現場の管理指標に翻訳できます。人数は誰でも数えられるので、時計を持たずに運用できる指標になります。

適用にあたっての注意は3つあります。1つ目は定常状態であることです。開店直後の立ち上がりや、閉店間際の駆け込みのように、系の中の数が一方向に増減している最中の時間帯に当てはめると、値がずれます。適用するのは、混雑が落ち着いて出入りが釣り合っている時間帯です。第9章では、この立ち上がり期間をシミュレーションでどう扱うかを扱います。

2つ目は単位を揃えることです。到着率を「1時間あたり」で測っておきながら、滞在時間を「分」で答えると60倍ずれます。単純な話ですが、現場のデータは日次・時間次・分次が混在しているため、実際によく起きます。3つ目は、系の境界をどこに引いたかを明示することです。「待ち行列だけ」なのか「サービス中も含めた滞在全体」なのかで、\( L \) も \( W \) も変わります。境界の定義を書かずに数字だけを引き渡すと、受け取った側が別の意味で解釈します。

利用率の非線形性、0.9を超えたところで何が起きるか

利用率は、処理能力のうちどれだけが実際に使われているかを表す比率です。稼働率とも呼ばれます。到着率を \( \lambda \)、1台あたりのサービス率を \( \mu \)、窓口数を \( c \) とすると、\( \rho = \lambda / (c\mu) \) で定義されます。\( \rho \) が1を超えると、入ってくる量が処理能力を上回るので列は無限に伸びます。したがって現実に成り立つのは \( \rho < 1 \) の範囲だけです。

M/M/1 の場合、平均待ち時間は \( W_q = \dfrac{\rho}{\mu (1-\rho)} \) という式で与えられます。分母に \( 1-\rho \) があるところが要点です。利用率が1に近づくと分母がゼロに近づき、値が急激に大きくなります。この形を数値で確かめるコードを実行しました。

import numpy as np
from math import factorial

def erlang_c(c, a):
    """呼量aのときにc席で待たされる確率(アーランC式)"""
    top = a ** c / factorial(c) * (c / (c - a))
    bottom = sum(a ** k / factorial(k) for k in range(c)) + top
    return top / bottom

MU = 1.0
print("【1台のとき】稼働率と平均待ち時間(M/M/1)")
print("%-10s %-16s %-10s" % ("稼働率", "平均待ち時間", "前の行の何倍"))
prev = None
for rho in (0.50, 0.70, 0.80, 0.90, 0.95, 0.98):
    wq = rho / (MU * (1 - rho))
    ratio = "-" if prev is None else "%.2f倍" % (wq / prev)
    print("%-10.2f %-16.2f %-10s" % (rho, wq, ratio))
    prev = wq

print("\n【台数を増やしたとき】同じ稼働率0.90でも台数が多いほど待たない")
print("%-8s %-10s %-16s" % ("台数", "呼量a", "平均待ち時間"))
for c in (1, 2, 5, 10, 20):
    a = 0.90 * c
    pw = erlang_c(c, a)
    wq = pw / (c * MU - a)
    print("%-8d %-10.1f %-16.3f" % (c, a, wq))

実行結果は次のとおりです。サービス率を \( \mu = 1 \) としているので、時間の単位は「平均サービス時間1回分」です。1回の処理に5分かかる窓口なら、表の数値に5を掛けると分になります。

【1台のとき】稼働率と平均待ち時間(M/M/1)
稼働率        平均待ち時間           前の行の何倍
0.50       1.00             -
0.70       2.33             2.33倍
0.80       4.00             1.71倍
0.90       9.00             2.25倍
0.95       19.00            2.11倍
0.98       49.00            2.58倍

【台数を増やしたとき】同じ稼働率0.90でも台数が多いほど待たない
台数       呼量a        平均待ち時間
1        0.9        9.000
2        1.8        4.263
5        4.5        1.525
10       9.0        0.669
20       18.0       0.275
利用率平均待ち時間(サービス時間の倍数)利用率0.50からの倍率読み方
0.501.001.0倍半分空いている。待ちはサービス1回分
0.702.332.3倍まだ穏やか。日常的に許容される水準
0.804.004.0倍体感で「混んでいる」と言われ始める
0.909.009.0倍待ちがサービス時間の9倍。苦情が出る
0.9519.0019.0倍列が伸びたまま戻らない
0.9849.0049.0倍実質的に破綻。少しの変動で崩れる

この表の含意は、経営の常識と正面からぶつかります。設備投資をした以上、稼働率は高いほど良いという考え方は、資産効率の観点では正しいものです。しかし利用率0.50から0.90へ引き上げると、平均待ち時間は1.00から9.00へ、つまり9倍になります。稼働率という指標だけを見ていると、この9倍は視界に入りません。稼働率は0.40ポイント上がっただけで、指標としては健全な改善に見えるからです。

さらに悪いことに、悪化のペースは高稼働域ほど速くなります。0.90から0.95へは0.05ポイントの上昇ですが、待ちは9.00から19.00へと2.11倍になります。0.95から0.98への0.03ポイントで、19.00から49.00へと2.58倍です。稼働率を「あと少しだけ」上げる判断が、リードタイムを倍にします。稼働率の目標値を決める会議で、この非対称性が共有されていないと、現場が達成できない目標を背負うことになります。

もう1つ、この式が教える実務的な事実があります。高稼働域では、推定の誤差そのものが致命的になります。利用率の推定が0.90か0.95かで待ち時間の予測は2倍違いますが、稼働率0.90と0.95を実測で区別するのは容易ではありません。到着率の測定が5パーセントずれれば、待ち時間の見積もりは倍近くずれます。高稼働で運用している系については、点推定だけでなく、幅を持った見積もりを出す必要があります。

横軸に利用率0.5から0.98

規模の効果、窓口を分けずに1列にまとめる

実行結果の後半は、利用率を0.90に固定したまま、窓口の台数だけを変えたものです。台数が1台のとき待ちは9.000ですが、2台で4.263、5台で1.525、10台で0.669、20台で0.275まで下がります。利用率は6行すべて0.90で同じです。混み具合が同じでも、規模が大きいほど待たない、ということです。

窓口数呼量 a平均待ち時間1台のときからの短縮
10.99.000基準
21.84.26353パーセント減
54.51.52583パーセント減
109.00.66993パーセント減
2018.00.27597パーセント減

ここで使っている呼量 \( a \) は、到着率をサービス率で割った量で、\( a = \lambda / \mu = c\rho \) の関係にあります。窓口が20台で利用率0.90なら呼量は18.0です。コード中のアーランC式は、この呼量のもとで「到着した客が1台も空いていない状況に出くわす確率」を返します。その確率を余った処理能力 \( c\mu - a \) で割ると平均待ち時間になります。コールセンターの席数設計で長く使われてきた式です。

なぜ規模が大きいほど待たないのかは、変動の相対的な大きさで説明できます。窓口が1台のとき、その1台が長い処理に捕まると、後ろの全員が待たされます。20台あれば、1台が長い処理に入っても残り19台が回り続けるので、影響が薄まります。台数が増えるほど、個々のばらつきが全体としては打ち消し合うわけです。これがプーリングと呼ばれる効果です。

この結果は、現実の行列運用の根拠になります。窓口ごとに別々の列を作らせると、それぞれが独立した1台の系になります。空いている窓口の隣で別の列が伸びている、という状況が起きます。1本の列にまとめて、空いた窓口へ順に案内すれば、同じ台数のまま待ち時間が下がります。銀行や空港の保安検査で採用されている方式です。設備も人員も増やさずに、並ばせ方だけで改善できる数少ない打ち手です。

ただし、プーリングが効くには条件があります。どの窓口でもどの用件を処理できること、つまりスキルが互換であることが前提です。専門的な手続きが1つの窓口でしかできないなら、その用件についてはプーリングの効果は働きません。また、待っている人が窓口まで移動する距離や時間が無視できることも必要です。工場のように物理的な移動や段取り替えが伴う場合、まとめる効果は移動コストによって相殺されます。表の数値をそのまま自社に当てはめる前に、この2条件を確認する必要があります。

もう1点、逆方向の注意もあります。規模の効果があるということは、小さな系ほど待ちに弱いということでもあります。同じ利用率で運営していても、1台しかない設備や1人しかいない担当は、大きな部署と同じ稼働率目標では成立しません。人員の少ない拠点や、専用機に依存した工程は、目標稼働率を意識的に低く設定しておく必要があります。この点が、拠点別の目標を一律に置く運用の弱点になります。

キングマンの近似式、待ちは3つの因子の掛け算になる

ここまでは到着もサービスも指数分布という前提でした。現実の系はそうとは限りません。到着が予約制で均されている場合もあれば、まとめて到着する場合もあります。サービス時間も、機械加工のようにほぼ一定のものから、問い合わせ対応のように大きくばらつくものまであります。この一般的な場合を扱う近似が、キングマンの近似式です。

式は次の形になります。

\( W_q \approx \dfrac{\rho}{1-\rho} \cdot \dfrac{C_a^2 + C_s^2}{2} \cdot E[S] \)

\( C_a \) は到着間隔の変動係数、\( C_s \) はサービス時間の変動係数です。変動係数は標準偏差を平均で割った値で、ばらつきの大きさを平均に対する比率で表したものです。\( E[S] \) は平均サービス時間です。この式が優れているのは、待ち時間が3つの独立した因子の掛け算で書けている点にあります。

因子意味下げる打ち手効き方
\( \dfrac{\rho}{1-\rho} \)混み具合能力を増やす、需要を分散させる高稼働域で急激に効く。0.95を0.90にするだけで半減
\( \dfrac{C_a^2 + C_s^2}{2} \)ばらつき標準化、予約制、平準化、段取り短縮比例して効く。設備投資が要らない
\( E[S] \)処理時間そのもの作業改善、自動化、事前準備比例して効く。能力向上として \( \rho \) も下げる

この式で M/M/1 の場合を確認すると、指数分布の変動係数は1なので、中央の項は \( (1+1)/2 = 1 \) になります。残るのは \( \dfrac{\rho}{1-\rho} \cdot E[S] \) で、これは先ほど数値で見た M/M/1 の式そのものです。近似式が既知の厳密解と一致することが確認できます。逆に、サービス時間が完全に一定なら \( C_s = 0 \) なので中央の項は \( (1+0)/2 = 0.5 \) となり、待ちが半分になります。M/D/1 の待ちが M/M/1 の半分だという先ほどの記述と整合します。

近似式である以上、値そのものは厳密ではありません。特に利用率が低い領域では誤差が大きくなります。しかし実務で使いたいのは、たいてい混んでいる領域の見積もりです。利用率が高いところではこの近似はよく当たることが知られており、桁を掴む用途には十分です。厳密な数値が必要になったときに、シミュレーションへ移ればよいという役割分担になります。

キングマンの近似式を3つの箱の掛け算として示す概念図

ばらつきを減らす打ち手が、設備を増やさずに効く

キングマンの近似式から読み取るべき最大の実務的含意は、待ち時間を減らす方向が3つあるということです。混み具合を下げる、ばらつきを減らす、処理時間そのものを短くする。このうち混み具合を下げる打ち手は、多くの場合で設備投資か人員増を意味します。予算が要りますし、実行までに時間もかかります。一方でばらつきを減らす打ち手は、運用の設計変更だけで実行できるものが多くあります。

到着のばらつきを減らす代表的な打ち手が予約制です。来る人が自由なタイミングで来る状態では、到着間隔の変動係数は1前後になります。時間枠を決めて受け付ければ、到着間隔が均され、変動係数は下がります。病院の診療予約や、来店予約、サポート窓口のコールバック方式は、いずれもこの効果を狙ったものです。予約制は顧客の自由度を下げるので歓迎されないこともありますが、待ち時間の短縮という見返りがあります。

製造や物流でいえば、ロットの平準化が同じ役割を果たします。まとめて大量に流すと、到着が塊になり変動係数が跳ね上がります。小さなロットで均等に流せば、後工程から見た到着は滑らかになります。逆に、大ロットで流す判断は段取り替えの回数を減らすためになされることが多く、その意味では処理時間の短縮を狙った打ち手です。3つの因子のどれを取ってどれを捨てているのかを、式の形で意識しておくと判断がぶれません。

サービス時間のばらつきを減らす打ち手としては、作業の標準化と、例外処理の切り出しがあります。同じ窓口で簡単な用件と複雑な用件の両方を扱うと、サービス時間の分布は裾が長くなり、変動係数が大きくなります。複雑な用件を別の窓口や別の時間帯に分けると、通常の窓口のばらつきが小さくなります。ただしこれは窓口を分けることでもあるので、前節のプーリングの効果とは逆方向に働きます。どちらが勝つかは数値を入れてみないと分かりません。この種の相反する効果を評価するのが、シミュレーションの得意な領域です。

段取り時間の短縮は、処理時間とばらつきの両方に効きます。段取り替えが長く、しかも回数が読めないと、後工程から見たサービス時間は大きくばらつきます。段取りを短くすれば平均も下がり、ばらつきも下がります。設備を増やす投資と比べたときの費用対効果を比較する材料として、キングマンの式は使えます。ただし、この式の値はあくまで近似なので、投資判断に使う最終的な数値は、モデルを作って確認するのが順当です。

式で解ける範囲と、シミュレーションが要る範囲

ここまで見てきた式は、いずれも強い前提の上に立っています。前提が満たされている限り、シミュレーションを書く必要はありません。逆に前提から外れた瞬間、式は使えなくなります。この境界を把握しておくことが、道具の選び方を誤らないための条件です。

式で解ける条件シミュレーションが必要になる条件
定常状態である。混雑が一定の水準で落ち着いている時間帯によって到着率が変わる。昼のピークと夜の閑散が同居する
待ち行列が1つだけである工程が連なっている。前工程の詰まりが後工程を止める
資源を1つだけ使う作業者と設備と治具を同時に確保しないと始められない
割り込みがない。全員が同じ扱いを受ける優先度がある。緊急案件が先に入り、進行中の作業を止める
到着とサービスが単純な分布に従う実データの分布が複数の山を持つ、あるいは裾が極端に長い
資源は常に使える故障と修理がある。保全の予定で能力が変わる
並んだ人は必ず最後まで待つ待ちきれずに諦める。別の窓口へ移る
物理的な配置を考えない搬送距離、通路の幅、置き場の容量に制約がある

右の列に1つでも当てはまれば、式による答えは近似の域を出ません。特に「工程が連なっている」という条件は、製造でも事務処理でもほとんどの現場に当てはまります。単独の待ち行列としては余裕のある工程でも、前工程が詰まって流れが止まれば実際の待ち時間は式の予測から外れます。ネットワーク状に連なった待ち行列にも理論はありますが、成立条件が厳しく、現場の制約をそのまま持ち込むことはできません。

「客が諦める」という条件も見落としやすいところです。諦めが発生すると、系に残る人数が減るので、観測される待ち時間は式の予測より短くなります。数字だけを見ると改善しているように見えますが、実際には需要を取りこぼしています。第4章で扱った窓口配置の実験でも、待ち時間と並んで離脱率を出力していました。待ち時間だけを指標にすると、この取りこぼしが見えなくなります。

時間帯で到着率が変わる場合は、しばしば「ピーク時間帯だけを切り出して定常とみなす」という近似が使われます。ピークが十分に長く続くなら、この近似は実用に耐えます。しかしピークが短時間で終わる場合、系が定常に達する前にピークが過ぎるため、式は過大な待ち時間を予測します。逆に、ピークで積み上がった列がピーク後も残る影響は式に入りません。ピークの長さと系の反応の速さを比べて、どちらの誤差が効くかを判断する必要があります。

手計算で桁を掴んでから、モデルを作る

実務での順序としては、まず手計算で見立てを立て、その後にシミュレーションで精査することを勧めます。理由は3つあります。

  • 桁が分かる。窓口を1台増やすべきなのか10台増やすべきなのかは、式で判断できます。10台の話をしているときに1台単位の検討をしても意味がありません
  • そもそもモデルを作る必要があるかが分かる。式で十分に答えが出る問いなら、シミュレーションの工数をかける理由はありません
  • モデルの誤りに気づける。作ったモデルを単純な条件に落として理論値と比べれば、実装の間違いが検出できます

3つ目が特に重要です。シミュレーションのモデルは、書いた本人にも正しさが見えにくいという性質があります。動いて数字が出てくること自体は、正しさの証拠になりません。ところが理論値が分かっている条件に落とし込めば、答え合わせができます。第4章では、利用率0.8の M/M/1 について理論値4.0分に対してシミュレーションが4.0307分、95パーセント信頼区間が3.9541から4.1073という結果を示しました。理論値が区間に入っているので、実装が理論と矛盾していないことが確認できます。

この確認は、モデルの複雑な部分を検証するものではありません。工程の連鎖も、優先度も、故障も入っていない条件での確認です。しかし、この最も単純な条件で理論値と合わないモデルは、複雑な条件では確実に間違っています。逆に言えば、ここで合わせておくことが、以降の検証の土台になります。第10章で扱う検証と妥当性確認の手続きでは、この「解析解との突き合わせ」が最初に行う確認として位置づけられます。

順序を逆にした場合の失敗は分かりやすい形で現れます。理論的な見立てを持たずにモデルだけを作ると、出てきた数字が妥当なのかどうかを判断する基準がありません。待ち時間が30分と出たとき、それが正しいのかモデルの不具合なのかを切り分けられないまま、数字が独り歩きします。手計算での見立てを先に持っていれば、桁が違う時点で立ち止まれます。式は答えを出す道具であると同時に、答えを疑うための道具でもあります。

本章で扱った内容を一言でまとめると、待ち時間は混み具合とばらつきと処理時間の3つで決まり、そのうち混み具合の効き方だけが非線形である、ということになります。稼働率を高く保つ運営は資産効率の観点では正しく、リードタイムの観点では危険です。どこで折り合いをつけるかは経営判断ですが、判断の材料として、0.90と0.95の間に横たわる2倍の差は共有されているべきものです。そして、この式の前提から外れる条件が現場にいくつあるかを数えたとき、その数が多いほど、シミュレーションを作る理由が強くなります。

この章を深めたい方への参考書籍

待ち行列理論の基礎と応用(川島幸之助 監修、塩田茂雄・河西憲一・豊泉洋・会田雅樹 著、共立出版)
待ち行列理論の基礎から、コールセンタ設計や通信網の性能評価といった応用までを扱います。本章で式の形だけ示した部分を、導出から追いたい方に向きます。

わかりやすい待ち行列システム(高橋敬隆 ほか、電子情報通信学会 編、電子情報通信学会(コロナ社発売))
待ち行列を理論と実践の両側から説明した本です。式が現場のどの数字に対応するのかを結びつけたいときに向きます。

第6章 エージェントベースモデリング、個の行動から全体が立ち上がる

ここまでの章で扱ってきたモデルは、いずれも「全体の仕組みを、こちらが先に書く」型でした。モンテカルロ法では入力の確率分布と計算式を与え、離散イベントシミュレーションではプロセスの順序と資源の使い方を与えました。作り手が全体の構造を記述し、そこに乱数を流し込んで結果を見る、という関係です。

本章で扱うエージェントベースモデリングは、この関係が逆になります。作り手が書くのは個々の主体の行動ルールだけです。全体がどう動くかは書きません。個々の主体を大量に用意し、互いに影響しあわせながら時間を進め、その結果として立ち上がってくる全体の挙動を観察する。全体は入力ではなく、出力です。

この「全体を書かない」という設計は、慣れるまで奇妙に見えるかもしれません。全体の挙動を知りたいのに、全体を記述しないのですから。しかし、この設計でなければ捉えられない現象があります。人がお互いを見て真似をする、避ける、学ぶ、といった相互作用が結果を決めている場合、全体の平均的な振る舞いを式で書き下そうとしても、そもそもその式が存在しないことが多いのです。本章では、その理由と、実務でどう使い、どこで慎重になるべきかを扱います。

個にルールを与え、全体を観察するという設計

エージェントベースモデリングの構成要素は、大きく3つです。第一にエージェント、すなわち独立して判断する主体です。顧客、従業員、店舗、車両、世帯、企業といった単位が該当します。第二に環境、すなわちエージェントが置かれる場です。格子状の空間であることもあれば、後述するネットワークであることもあり、単に「全員が全員と接触しうる」という設定のこともあります。第三に相互作用のルールです。誰と接するか、接した相手から何を受け取るか、それを受けて自分の状態をどう変えるかを決めます。

時間の進め方は、多くの場合ステップ方式を採ります。第1期に全エージェントが自分のルールに従って状態を更新し、第2期にまた更新し、という繰り返しです。第4章で扱った離散イベントシミュレーションのように「次のイベントまで時計を飛ばす」形を採ることもありますが、エージェントベースでは「全員が周囲を見て、同時に判断する」という更新のほうが素直に書けるため、等間隔のステップが使われることが多くなります。

ルールの記述量は、驚くほど少なくて済むことがあります。後で紹介する古典的なモデルは、いずれも数行で説明できる程度のルールしか持っていません。それでも、そこから出てくる集団の挙動は、ルールを見ただけでは予想できないものになります。この落差こそがエージェントベースモデリングの中心にある性質です。

もう1つ重要な特徴があります。エージェントは異質でよい、という点です。全員が同じ性格を持つ必要はありません。値引きに敏感な顧客とそうでない顧客、周囲に流されやすい人とそうでない人を、同じモデルの中に混在させられます。集計的なモデルでは「平均的な顧客」を1つ立てて代表させますが、エージェントベースでは個体差をそのまま持たせられるため、「一部の層だけが強く反応した結果、全体が動く」という筋道を表現できます。

流されるものと、自分で決めるもの

離散イベントシミュレーションとの違いは、実務で手法を選ぶときに最初に効いてくるところなので、はっきりさせておきます。

離散イベントシミュレーションに登場するエンティティ、たとえば工程を流れる部品や、窓口に並ぶ客の伝票は、流されるものです。どの工程をどの順で通るか、どの資源を使うか、次に何が起きるかは、モデルの外側にあるプロセス定義が決めています。エンティティ自身は判断しません。到着し、待ち、処理され、去る。その筋書きは作り手が書いた通りに進みます。

エージェントは、これに対して自分で決めるものです。周囲の誰と接するかを選び、接した相手の状態を見て、自分がどう振る舞うかを決めます。同じ状況に置かれても、性格を表すパラメータが違えば違う選択をします。プロセスの順序は外から与えられておらず、各自の判断の積み重ねとして生じます。

同じ現場でも、問いによって適する側が変わります。工場を例に取るなら、部品がどの順で工程を通り、どこで滞留し、装置の前に何個たまるかを知りたいのであれば離散イベントです。部品は判断しませんし、知りたいのは資源の取り合いだからです。一方、同じ工場で、作業者どうしが忙しい持ち場を見つけて応援に入る仕組みを設計したい、あるいは新しい手順が熟練者から新人へどう伝わっていくかを見たい、という問いであれば、判断するのは作業者ですから、エージェントベースが向きます。

この対比を、いくつかの観点で整理しておきます。

観点離散イベントシミュレーションエージェントベースモデリング
主役エンティティ。流されるものエージェント。自分で決めるもの
作り手が書くものプロセスの順序と資源の制約個体の行動ルールと接触の相手
全体の挙動与えたプロセスから直接たどれる書いていない。結果として現れる
時間の進め方次のイベント時刻へ飛ばす等間隔のステップが多い
個体差属性として持てるが判断はしない判断のしかた自体が個体ごとに違ってよい
典型的な出力待ち時間、稼働率、滞留数普及率、分居の度合い、集団の形
向く問い資源をいくつ用意すべきかどういう条件なら広がるか

両者は排他ではありません。実務では、判断する主体と流れる仕事の両方が同時に存在する場面が多く、その場合は片方の中にもう片方を埋め込む形になります。たとえば店舗のモデルで、客の店内での移動先の選択はエージェント的に書き、レジでの待ち行列は離散イベント的に書く、という組み合わせは自然です。手法の名前で線を引くよりも、「この対象は判断するのか、それとも流されるのか」を要素ごとに見分けるほうが、設計は素直に進みます。

離散イベントシミュレーションとエージェントベースモデリングの対比

個の平均は、全体の結果にならない

エージェントベースモデリングを語るときに必ず出てくる言葉が創発です。個々のルールは単純でも、集団として現れる挙動は単純ではない、という性質を指します。もう少し実務寄りに言い換えると、個の性質をいくら丁寧に調べても、集団の結果は算術的には出てこないということです。

この点を、よくある誤解と対比させると分かりやすくなります。新サービスの普及を検討するとき、多くの現場ではまず個人へのアンケートを取ります。「周りの何割が使っていたら、あなたも使いますか」と聞き、回答の平均が仮に3割だったとします。ここから「3割の人が使い始めたら、そこから全体に広がる」と結論したくなりますが、この推論は成立しません。

成立しない理由は2つあります。1つは、平均を取った時点で個体差が消えるからです。実際には、周囲の1割で動く人と、8割にならないと動かない人が混ざっています。普及が起きるかどうかを決めるのは平均値ではなく、「最初に動く少数がいるか」「その人たちの隣に、次に動く人がいるか」という連鎖の有無です。もう1つは、誰と誰が隣り合っているかという構造が結果を左右するからです。同じ人数、同じ性格の分布でも、つながり方が違えば普及したりしなかったりします。

経済学ではこれを合成の誤謬と呼びます。個々にとって合理的な行動が、全員が同じことをすると全体としては望ましくない結果を招く、という現象です。エージェントベースモデリングが扱うのは、この「個の性質から全体の性質が単純には導けない」領域そのものです。だからこそ、集計的なモデルで代用できません。全体を1つの式や1つのストックで表そうとした瞬間に、個体差と相互作用という、結果を決めている当のものが落ちてしまうからです。

逆に言えば、個体差と相互作用が結果を決めていない問題には、この手法は要りません。全員がほぼ同じように振る舞い、他人の行動に影響されないのであれば、平均で代表させても答えは変わりません。エージェントベースモデリングを持ち出す前に、「この問題では、人が互いを見て行動を変えているか」を確認するのが先です。見ていないなら、もっと軽い手法で足ります。

3つの古典モデルが示した一般則

この分野には、繰り返し参照される古典的なモデルがいくつかあります。ここでは3つを取り上げます。いずれも特定の企業や事例を説明するためのものではなく、「こういう性質が一般に成り立つ」という形の知見を残した点に価値があります。

シェリングの分居モデルは、トーマス・シェリングが1971年に発表したものです。格子状の街に2種類の住民を配置し、各住民は「自分の近隣のうち、同じ種類の割合が一定を下回ったら引っ越す」というルールだけを持ちます。このモデルが示した一般則は、個人が持つ選好が弱くても、集団としては強い分居が生じうるということです。誰も分離した街を望んでいなくても、「近所に自分と同じ人がある程度はいてほしい」という程度の弱い希望が全員にあるだけで、結果として街はきれいに分かれてしまいます。ここから引き出せる実務上の含意は、集団に現れたパターンを見て、個々の意図がそれと同じくらい強かったと推測してはいけない、という点です。

ボイドの群れモデルは、クレイグ・レイノルズが1987年に発表したものです。各個体は3つの局所ルールだけを持ちます。近すぎる相手からは離れる、周囲の相手と向きをそろえる、周囲の重心のほうへ寄る、の3つです。この3つだけで、鳥や魚の群れのように滑らかにまとまり、障害物を避けて分かれ、また合流する動きが現れます。示した一般則は、全体を統率する司令塔がなくても、局所的なルールだけで秩序ある集団行動が生じるということです。全員が近くの数個体しか見ていないのに、群れ全体としては一貫した動きになります。

3つめが、本章のコードで扱うしきい値モデルです。マーク・グラノヴェッターが1978年に定式化したもので、各個人は自分固有のしきい値を持ち、周囲で採用している人の割合が自分のしきい値を超えたら、自分も採用するという単純なルールで動きます。しきい値が0に近い人は誰も使っていなくても始める人で、しきい値が高い人は大勢が使ってからでないと動かない人です。このモデルが示した一般則は、集団の反応は、しきい値の平均ではなく、しきい値の分布と並び方で決まるということです。平均が同じでも、しきい値の低い人が連鎖できる位置にいるかどうかで、結果は大きく変わります。

3つに共通しているのは、いずれも「特定の状況を再現した」のではなく「条件を満たす場面では一般にこうなる」という形の主張を残していることです。エージェントベースモデリングの成果物として何を期待すべきかも、ここに表れています。細部まで現実に一致した予測値ではなく、条件と結果の対応関係、つまりどういう条件のときにどういう型の結果が起きるか、という知見を取り出す手法だと考えるのが実態に合っています。

スモールワールドの網の上で普及を試す

ここからは、しきい値モデルを実際に動かして性質を確かめます。エージェントを置く場として、ワッツ・ストロガッツのスモールワールドネットワークを使います。1998年にダンカン・ワッツとスティーヴン・ストロガッツが提案したもので、まず全員が近所の一定数とだけつながった輪を作り、その一部のつなぎ先を確率的に遠くへ張り替えて作ります。こうすると、ほとんどのつながりは近所どうしのまま残るのに、少数の遠距離のつながりのおかげで、任意の2人が短い経路で結ばれる網ができます。現実の知人関係や組織内の連絡経路が持つ性質に近いため、複雑ネットワークの研究では標準的な題材として使われます。

実験の設計は単純にしました。2000人を配置し、1人あたり平均8本のつながりを持たせ、そのうち1割を張り替えます。初期採用者は全体の2%、つまり40人を無作為に選び、これはどの条件でも同じにします。動かすのはしきい値だけです。全員が同じしきい値を持つとして、その値を0.20から0.34まで変えたときに、最終的に何割まで普及するかを見ます。乱数の種を0から19まで変えて20回試し、それぞれの最終普及率を集計しました。

import numpy as np
import networkx as nx

def run(threshold, n=2000, k=8, p_rewire=0.1, seed=0, steps=40):
    """しきい値モデル:近隣の採用割合がしきい値を超えたら自分も採用する。"""
    rng = np.random.default_rng(seed)
    g = nx.watts_strogatz_graph(n, k, p_rewire, seed=seed)
    adopted = np.zeros(n, dtype=bool)
    seeds = rng.choice(n, size=int(n * 0.02), replace=False)   # 初期採用者2%
    adopted[seeds] = True
    history = [adopted.mean()]
    for _ in range(steps):
        nxt = adopted.copy()
        for i in range(n):
            if adopted[i]:
                continue
            nb = list(g.neighbors(i))
            if nb and np.mean(adopted[nb]) >= threshold:
                nxt[i] = True
        if (nxt == adopted).all():
            adopted = nxt
            history.append(adopted.mean())
            break
        adopted = nxt
        history.append(adopted.mean())
    return history

print("初期採用者はどの行も同じ2%。しきい値だけを動かす。")
print("%-10s %-14s %-14s %-10s" % ("しきい値", "最終普及率(%)", "普及した試行", "止まるまで"))
for th in (0.20, 0.24, 0.26, 0.28, 0.30, 0.34):
    finals = [run(th, seed=s)[-1] for s in range(20)]
    spread = sum(1 for f in finals if f > 0.5)
    print("%-10.2f %-14.1f %-14s %-10d"
          % (th, np.mean(finals) * 100, "%d/20" % spread, len(run(th, seed=0)) - 1))

コードの中心は、内側の二重ループです。各ステップで、まだ採用していない全員について隣人の採用割合を計算し、しきい値以上であれば採用に切り替えます。更新はnxtという別の配列に書き込んでおり、同じステップの中で先に更新された人の影響が、同じステップの後ろの人に及ばないようにしています。全員が同じ瞬間に周囲を見て判断する、という設定です。誰も新たに採用しなくなったら、そこで止まります。

実行結果は次のとおりです。

初期採用者はどの行も同じ2%。しきい値だけを動かす。
しきい値       最終普及率(%)       普及した試行         止まるまで
0.20       100.0          20/20          19
0.24       100.0          20/20          29
0.26       35.6           7/20           40
0.28       34.6           5/20           40
0.30       4.0            0/20           1
0.34       2.2            0/20           1

表の読み方を補っておきます。「最終普及率」と「普及した試行」は20回の試行をまとめた値で、「普及した試行」は最終普及率が50%を超えた回数です。いちばん右の「止まるまで」だけは種を0に固定した1回分の値で、何ステップで動きが止まったかを表します。ステップ数の上限は40に設定しているため、40と出ている行は上限で打ち切られた、つまりまだゆっくり広がっている途中だったことを意味します。逆に1と出ている行は、最初の1ステップで誰も新たに採用せず、その場で止まったことを意味します。

結果の読み方、平均1本に潰してはいけない

この表を「しきい値が上がると普及率が下がる」とだけ読むと、いちばん大事なところを取り逃がします。平均の裏側にあるものを見るために、20回の試行の最終普及率を1つずつ並べて確かめました。以下は同じコードで種を0から19まで変え、各回の最終普及率をそのまま取り出した実測値です。

しきい値0.26の20回は、小さい順に2.0、2.1、10.7、12.8、15.8、17.2、18.0、24.2、25.8、26.6、33.1、39.3、39.6、50.7、52.2、56.0、62.3、64.9、66.0、92.5となりました。単位は%です。しきい値0.28の20回も同じ傾向で、最小は2.0、最大は89.0でした。

まず目につくのは、この20個が2.0%から92.5%まで、ほぼ切れ目なく散らばっていることです。最小と最大で46倍の開きがあります。しかも条件は何ひとつ変えていません。しきい値も、初期採用者の2%という割合も、ネットワークの作り方も同じです。変えたのは乱数の種だけで、実質的な違いは「最初の40人がたまたま誰だったか」と「遠距離のつながりがたまたまどこに張られたか」の2つしかありません。それだけで、ほとんど広がらない結果から9割方まで届く結果までが出ます。

次に確認すべきは、平均の35.6%が起きやすい値ではないという点です。20回のうち30%台に着地したのは33.1、39.3、39.6の3回だけで、残る17回はそれより下か、それよりかなり上に散っています。35.6%は20個をならして作った数字であって、「だいたいこのくらいに落ち着く」という意味の代表値ではありません。ところが報告書に「普及率は35.6%と見込まれます」と書けば、読み手はほぼ確実に後者の意味で受け取ります。数字が正しく計算されていても、伝わる内容が事実と違ってしまうわけです。

しきい値ごとに散らばり方がどう変わるかを整理します。平均と「50%を超えた回数」は先の実行結果の通りで、最小・最大と「1人も増えなかった回数」は同じ20回の値から数えたものです。最後の列は、初期採用者の40人から採用者が1人も増えないまま止まった回の数を指します。

しきい値20回の最小20回の最大平均50%を超えた回1人も増えなかった回
0.20100.0%100.0%100.0%20回0回
0.24100.0%100.0%100.0%20回0回
0.262.0%92.5%35.6%7回1回
0.282.0%89.0%34.6%5回1回
0.302.0%16.4%4.0%0回14回
0.342.0%5.1%2.2%0回18回

この表が示しているのは、しきい値0.24から0.26への変化が「普及率が下がった」のではなく「結果が定まらなくなった」という質の変化だということです。0.24までは20回とも100.0%で、ばらつきがまったくありません。何回まわしても同じ答えが出る領域です。0.26に上げた途端、20回が2.0%から92.5%まで散ります。平均が100.0から35.6へ落ちた、という読み方をすると、この質の変化がまるごと隠れます。落ちたのは水準だけではなく、予測できるという性質そのものです。

さらに0.30まで上げると、今度は逆側で再び結果が定まります。20回のうち14回は初期採用者の40人から1人も増えず、最も広がった回でも16.4%です。0.34では18回が1人も増えません。つまり、しきい値を動かしていくと、必ず広がる領域、広がるかどうか決まらない領域、ほぼ広がらない領域の3つを順に通ります。臨界点という言葉は、この領域の境目を指すものとして使うのが正確です。そして経営にとって重要なのは、境目の内側、つまり結果が定まらない領域では、平均や期待値を計算しても意思決定の役に立たない、という点です。予測できる領域と予測できない領域があり、その境目はパラメータのわずかな差でまたいでしまいます。

なぜ0.02動かしただけで領域が変わるのかは、コードの条件式を見ると分かります。判定は隣人の採用割合がしきい値以上かどうかですが、隣人の数は整数です。このネットワークでは1人あたりの平均が8本で、実際の本数は5本から12本の間に分布し、最も多いのは8本ちょうどの人です。隣人が8人の人にとって、しきい値0.24なら2人が採用していれば足りますが、0.26になると3人必要になります。この0.02の差が、参加者の半数以上について「必要な採用者が1人増える」という変化に化けます。同様に、0.28から0.30への変化は、隣人が7人の層について必要人数を2人から3人へ押し上げます。連続的に見えるパラメータが、実際には離散的な条件に翻訳されているため、結果も段階的に飛ぶわけです。

3つめの読み取りは、ここまでの2つから自然に出てきます。分布は描いて確かめるしかないということです。実行結果の表が出しているのは、平均と「50%を超えた回数」という2つの要約だけでした。この2つからは分布の形が決まりません。平均が35.6%で50%超が7回という情報だけなら、「高いところと低いところに割れていて中間が空いている」という形も、「なだらかに散らばっている」という形も、どちらも矛盾なく描けてしまいます。実測はなだらかに散らばっているほうでしたが、それは20個の値を並べて初めて分かったことです。要約統計量から分布の形を推測してはいけない、という教訓としてそのまま使えます。山がいくつあるのか、裾がどこまで伸びるのかは、実際の値をヒストグラムに描いて目で見るまで分かりません。第9章で扱う出力の分析にも同じ注意が当てはまります。

意思決定への渡し方も変わります。結果が定まらない領域では、問いは「期待値はいくらか」ではありません。どれくらいの幅で振れるか、下振れしたときに何が起きるか、振れ幅を狭めるには何を変えればよいかの3つです。0.26の設定であれば、「20回まわすと2.0%から92.5%まで散らばり、半分を超えたのは7回、1割に届かなかったのが2回、最も広がった回でも全員には届かなかった」という形で渡します。この渡し方であれば、受け手は「うまくいけば大きい、外すとほぼゼロ、そしてどちらに転ぶかは今の設計では決められない」という状況を正しく把握できます。

そこから先は、振れ幅を狭める手立ての議論になります。初期採用者を2%より増やすのか、しきい値そのものを下げる施策を打つのか、初期採用者を無作為ではなく特定の位置に配置するのか。いずれも検討に値しますが、評価するときは平均がどれだけ上がったかではなく、散らばりがどれだけ縮んだかを見ます。平均が同じでも、20回とも同じような結果になる設計と、2%から92.5%まで散る設計とでは、事業として引き受けるリスクがまったく違うからです。

しきい値と最終普及率の関係を示すグラフ

業務でどこに使うか

適用領域は、「結果が個体差と相互作用で決まる」という条件で見分けられます。以下は手法として相性のよい問題の型を並べたものであり、特定企業の事例を指すものではありません。

  • 新製品・新サービスの普及。誰が先に使い、その人の周囲にどう伝わるかで結果が決まる問題です。本章の実験がそのまま対応します。既存の予測モデルは過去の普及曲線に当てはめる形が多く、まだ売っていないものには使えません。エージェントベースなら、接触の構造と個体差を仮定して条件を振れます。
  • 口コミとキャンペーンの設計。同じ予算を、広く薄くまくのか、一部に集中して初期採用者を厚くするのかで結果が変わります。臨界点の存在は、この配分の問題に直結します。配分を少し変えただけで、結果が定まる領域と定まらない領域を行き来しうるからです。平均的な効果だけを比べていると、この違いは見えません。
  • 価格改定に対する顧客の離反。値上げへの反応は個人差が大きく、しかも周囲の反応を見て決める部分があります。平均的な価格感度で計算すると、集団的な離反が起きる条件を見落とします。
  • 店舗内の客の動線と滞留。客は自分で進む先を選び、混んでいる場所を避けたり、逆に人だかりに寄ったりします。棚の配置を変えたときに滞留がどこへ移るかは、個々の選択の集まりとして現れます。
  • 避難行動と人流。出口の選択、他人への追随、通路での相互干渉が結果を決めます。定員や通路幅といった静的な計算だけでは、詰まりが発生する条件が見えません。
  • 感染症の接触ネットワーク。誰が誰と接触するかという構造が伝播を左右します。全員が均一に混ざると仮定した集計モデルと、接触構造を持たせたモデルとでは、対策の効き方の評価が変わります。
  • 社内の技能伝播と組織の学習。新しい手順や道具が組織に定着するかどうかは、部署をまたぐ人のつながりと、周囲がどれだけ使っているかへの感度で決まります。研修の対象者を誰にするか、という設計に対応づけられます。

これらに共通するのは、担当者の直感が「平均で考えると外れる」領域だという点です。平均的な顧客、平均的な感度、平均的な接触回数で計算した結論が、実際の集団の反応と食い違う。その食い違いの原因が個体差と相互作用にあるとき、エージェントベースモデリングが噛み合います。

逆に、この手法を持ち出すべきでない場面もはっきりしています。相互作用がほとんどない問題、たとえば設備の故障間隔や需要のばらつきを積み上げるだけなら、第3章のモンテカルロ法で足ります。資源の取り合いが主題なら第4章の離散イベントです。全体の総量の増減とその遅れが主題なら、第7章で扱うシステムダイナミクスのほうが記述量が少なくて済みます。エージェントベースは記述の自由度が高いぶん、作れてしまうモデルの範囲も広く、必要のない場面で使うと検証しきれない複雑さを抱え込むことになります。

難しさは、行動ルールの決め方にある

ここまでの説明で使いどころは見えたと思いますが、この手法にははっきりした難所があります。エージェントの行動ルールをどう決めるかです。

離散イベントシミュレーションであれば、処理時間の分布は実績データから推定できます。到着間隔も記録から取れます。第8章で扱う入力モデリングの手続きに乗せられる、ということです。ところがエージェントベースでは、決めなければならないのが「人がどういう条件で行動を変えるか」という内面のルールです。これを観測データから直接推定できることは、あまり多くありません。購買履歴からは何を買ったかは分かりますが、「周囲の何割が使っていたから買ったのか」は分かりません。アンケートで聞けば数字は得られますが、回答と実際の行動が一致する保証もありません。

その結果、モデルの中に「確認できていない仮定」が入ります。しかも、本章の実験が示した通り、その仮定のわずかな違いが結果の性質そのものを変えます。しきい値を0.24とするか0.26とするかで、「確実に広がる」と「広がるかどうか分からない」が入れ替わるのに、その値の根拠が薄い、という状態になりやすいのです。仮定が結果を支配するという言い方は誇張ではありません。

この難しさに対して、実務で採れる方針は3つあります。

  1. ルールは少なく、単純にする。 現実らしさを追ってルールを足していくと、どのルールが結果を決めているのか分からなくなります。ルールが1つ増えるごとに、根拠の薄い仮定が1つ増えます。まず最小限のルールで動かし、結果の型を把握してから、必要なものだけを足すほうが安全です。古典的なモデルがいずれも数行のルールしか持っていないのは、単に古いからではなく、そのほうが何が効いているか分かるからです。
  2. 結果はパラメータを振って、傾向として読む。 1つのパラメータ設定で計算するのではなく、本章の実験のように値を並べて振り、どこで挙動が変わるかを探します。得られる知見は「しきい値が0.25あたりを境に、結果が定まる領域から定まらない領域へ移る」という形になります。この形の知見は、しきい値の真の値が分からなくても使えます。現在の水準がその境目のどちら側にありそうかを議論でき、定まる側へ寄せるために何を変えるべきかも議論できるからです。
  3. 1本の数字を答えとして出さない。 「普及率は35.6%と予測されます」という報告は、この手法では避けるべき出し方です。数字が正しく計算されていても、その数字が意味するものが受け手の想定と食い違います。「20回まわすと2.0%から92.5%まで散らばり、半分を超えたのは7回だった」という形で、散らばりごと渡すほうが、意思決定には役に立ちます。

3つの方針はいずれも、モデルをどこまで信じてよいかを扱う話につながります。エージェントベースモデリングでは、モデルの出力が現実の数値と一致することを確かめる形の検証が難しい場合が多く、代わりに「ルールが専門家の理解と矛盾しないか」「既知の一般則を再現するか」といった別種の確認に頼ることになります。この確認の手続き全体については、第10章の検証と妥当性確認で扱います。少なくとも、根拠の薄い仮定を含むモデルを、根拠の薄さを伝えずに数値だけ渡すことは避けなければなりません。

実装に使う道具についても触れておきます。Pythonで書く場合はMesaというフレームワークがあり、エージェントと環境、時間の進め方、データの収集といった定型部分を用意してくれます。本章のコードのように、NumPyとNetworkXだけで直接書いてしまうこともできます。教育と研究の分野で長く使われているものとしてNetLogoがあり、モデルを書いて動かし、パラメータを画面上のつまみで動かしながら挙動を見るところまで一体になっています。商用では、離散イベント、エージェントベース、システムダイナミクスの3つを1つのモデルの中で混ぜられるAnyLogicが知られています。どれを選ぶかは、既存の分析環境との接続や、誰が触るかによって変わります。ツールの選び方は第14章でまとめて扱います。

本章の要点をまとめます。エージェントベースモデリングは、個の行動ルールだけを書き、全体は結果として観察する手法です。個の平均から全体は導けないという性質があるため、集計的なモデルでは代用できません。その代わり、パラメータのわずかな違いで、結果が定まる領域から定まらない領域へ移る臨界点が現れやすく、その内側では平均値が集団の代表値として機能しなくなります。したがって、出力は分布として見て、分布として渡す。この作法が守られていれば、行動ルールに不確かさが残っていても、意思決定に使える知見を取り出せます。次章では、個ではなく全体の総量の増減に注目し、ストックとフローで構造の癖を読むシステムダイナミクスを扱います。

この章を深めたい方への参考書籍

人工社会構築指南 改訂新版(山影進、書籍工房早山)
エージェントベースのモデルを実際に組み立てる手順を扱う本です。個の行動ルールをどう書くかという、本章でいちばん難しい部分に踏み込めます。

社会シミュレーションの技法(Nigel Gilbert・Klaus G. Troitzsch 著、井庭崇・岩村拓哉・高部陽平 訳、日本評論社)
社会現象をシミュレーションで扱うための技法を整理した本です。個から全体が立ち上がる現象を、研究の系譜として捉え直せます。

第7章 システムダイナミクス、ストックとフローで構造の癖を読む

第6章では、個々の主体が持つ行動規則から全体の挙動が立ち上がる様子を見ました。本章で扱う手法は、それとは逆の方向から現実を捉えます。個体を1人ずつ、1個ずつ数えることをやめ、集計された量の増減だけで系を表します。在庫が何個あるか、現金がいくら残っているか、人員が何人いるか。それらが時間とともにどう増え、どう減るか。この粗い抽象化と引き換えに見えてくるのが、系そのものが持つ構造的な癖です。

この系譜は、1950年代末から1960年代にかけて、マサチューセッツ工科大学のジェイ・フォレスターが体系化したものです。もともとは電子工学と制御工学の考え方を企業経営に持ち込む試みで、1961年の著書『Industrial Dynamics』がその出発点になりました。制御工学の言葉で言えば、企業も工場もサプライチェーンも、入力に対して出力が返り、その出力がまた入力に戻ってくるフィードバック系です。フィードバック系には、増幅も、振動も、行き過ぎも起こります。それらは誰かの怠慢や判断ミスではなく、系のつなぎ方から自動的に生じます。

本章で最も伝えたいことは、この「誰のせいでもないのに悪い結果が出る」という現象の扱い方です。現場では、問題が起きると担当者の判断や努力に原因を求めがちです。しかし系の構造そのものが振動を作っているとき、担当者を入れ替えても、指示を強くしても、結果は変わりません。むしろ「もっと速く直せ」という指示が、振動を大きくすることがあります。この逆説を、実際に動くモデルの数値で確認していきます。

ストックとフロー、写真に写る量と期間で数える量

この手法で世界を表現するとき、使う部品は基本的に2種類しかありません。ストックとフローです。ストックは溜まっている量、フローは流れている量を指します。ストックはフローによって増減し、増減した結果のストックがまた次のフローの大きさを決めます。この往復だけで、驚くほど多くの現象が記述できます。

両者を見分ける実務的な方法があります。ストックは「今この瞬間に写真を撮ったら数えられるもの」です。倉庫の写真を撮れば在庫の数は数えられます。銀行口座の残高も、社員名簿の人数も、未返済の借入金額も、待合室に座っている人数も、その瞬間の値が確定します。一方フローは「一定の期間を区切らないと数えられないもの」です。写真を1枚撮っても入荷量は分かりません。「今週の入荷は何個か」「今月の採用は何人か」というように、必ず期間とセットでなければ意味を持ちません。

この区別は理屈の上では単純ですが、実務の議論では驚くほど混同されます。典型的なのが人員の話です。「人が足りない」という発言はストックの話をしています。今この瞬間に何人いるか、という話です。「採用が足りない」という発言はフローの話をしています。単位期間あたり何人入ってくるか、という話です。この2つは違う問題であり、打ち手も違います。人が足りない状態は、採用を増やしても、離職を減らしても、外部委託で必要人数そのものを下げても解消します。ところが議論が「採用が足りない」の一点に固定されると、離職側の穴が空いたまま採用を増やし続けることになり、投入した労力の割に人数が増えないという結果になります。

ストックとフローには、もう1つ実務上重要な性質があります。ストックは瞬間的には動かせないということです。フローは指示や意思決定で比較的すぐに変えられます。発注量は今日決めれば今日変わります。採用計画も、価格も、広告費も同様です。しかしストックは、フローが変化した後に、時間をかけて積み上がったり減ったりします。在庫を今すぐ倍にすることはできません。技術者を今すぐ10人増やすこともできません。この「フローは速く動き、ストックは遅れてついてくる」という非対称が、後で扱う振動の源になります。

領域ストック(瞬間に数えられる)フロー(期間で数える)
在庫管理倉庫在庫、店頭在庫、輸送中の在庫入荷量、出荷量、廃棄量
財務現預金、売掛金、借入残高売上、支出、返済額、減価償却費
人員在籍者数、有資格者数、稼働可能人数採用数、離職数、育成完了数
顧客契約継続中の顧客数新規獲得数、解約数
開発未対応の不具合件数、技術的負債の総量不具合の発生率、修正率
サポート未処理の問い合わせ件数着信件数、解決件数

表の各行を見ると、ストックの列に並んでいるものは、いずれも「今日の終業時点で報告できる数字」です。フローの列は「今週の実績」としてしか報告できません。会議に出てくる数字がどちらの種類なのかを最初に確認するだけで、議論の噛み合わなさはかなり減ります。ストックの目標とフローの目標を混ぜて掲げると、達成したのかどうかが判定できなくなります。

もう1つ付け加えると、財務諸表はこの区別を制度として固定したものです。貸借対照表はストックの一覧であり、損益計算書はフローの一覧です。会計を学んだ人がこの手法に馴染みやすいのは、同じ考え方を別の目的で使っているからです。違いは、会計が期末という決まった時点で記録するのに対し、この手法は時間を細かく刻んで両者の相互作用を追いかけるところにあります。

2種類のフィードバックループ、増やす輪と抑える輪

ストックとフローを結んで輪にしたものがフィードバックループです。ストックの水準がフローの大きさを決め、そのフローがストックを変え、変わったストックがまたフローを決める。この輪には2つの型しかありません。自己強化ループと均衡ループです。

自己強化ループは、増えるとさらに増える方向に働きます。口コミによる普及がその例です。利用者が増えると口コミの発信量が増え、口コミが増えると新規の利用者が増え、利用者がさらに増えます。複利の計算も同じ構造です。元本が増えると利息が増え、利息が元本に組み込まれて元本がさらに増えます。評判、市場シェア、学習の蓄積、規模の経済も、いずれもこの型です。この輪は逆回転もします。利用者が減ると口コミが減り、口コミが減ると利用者がさらに減ります。良い方向にも悪い方向にも加速するのが自己強化ループの特徴です。

均衡ループは、増えると抑える方向に働きます。在庫の補充がその代表です。在庫が目標より少なければ発注を増やし、多ければ発注を減らします。結果として在庫は目標の周りに保たれます。室温を一定に保つ空調も、価格が上がると需要が減って価格が下がる市場の調整も、人が増えすぎると採用を絞る人員計画も、同じ型です。均衡ループは系を安定させる働きをしますが、後述する遅れが入ると、安定させるどころか振動を作り出します。

働き単独で置いたときの挙動身近な例
自己強化ループ増えるとさらに増える指数的に増加、または指数的に減少口コミ、複利、評判、シェア争い、離職の連鎖
均衡ループ目標との差を埋めようとする目標値へ収束、遅れがあると振動在庫補充、価格調整、採用と離職、空調の温度制御

現実の系には、この2種類が同時に何本も走っています。市場に投入した新製品を考えると、口コミによる自己強化ループが普及を押し上げる一方で、市場の飽和という均衡ループが上限を作り、競合の参入という別の均衡ループが伸びを削ります。どの輪が支配的かは時期によって入れ替わります。初期は自己強化ループが強く、成熟期には均衡ループが強くなります。S字型の普及曲線は、この主導権の交代が生む形です。

実務でこの見方が役に立つのは、打ち手を「どの輪に働きかけるか」で整理できるところです。伸びを作りたいなら自己強化ループを回す設計をします。逆に暴走を止めたいなら均衡ループを効かせます。よくある失敗は、自己強化ループが逆回転している状況に、均衡ループ向けの対症的な手当てを繰り返すことです。解約が解約を呼ぶ状態に入っているのに、月ごとの新規獲得目標を積み増して埋め合わせようとすると、獲得コストが上がり続けるだけで構造は変わりません。

自己強化ループと均衡ループの対比図

遅れ、良かれと思った調整が振動に変わる仕組み

ここまでの説明では、ループを一周する間に時間がかからないものとして扱ってきました。現実にはそうなりません。発注してから届くまでには時間がかかります。採用してから戦力になるまでにも時間がかかります。広告を打ってから売上に効くまで、設備投資を決めてから稼働するまで、制度を変えてから現場の行動が変わるまで、いずれも遅れがあります。

遅れが厄介なのは、それが観測を歪めるからです。手を打った直後に結果を見ると、まだ何も変わっていません。効いていないように見えるので、もう一段強い手を打ちます。しばらくして、最初の手と2番目の手の効果が同時に出てきます。今度は行き過ぎているので、逆方向に手を打ちます。以下同様に、逆方向の行き過ぎが繰り返されます。振動の正体はこれです。判断そのものは毎回もっともらしく、その場では合理的です。それでも結果として系は揺れます。

遅れには2種類あります。1つは物理的な遅れで、輸送や生産や研修のように、実際に時間がかかるものです。もう1つは情報の遅れで、実際には変化しているのに、それがこちらに伝わるまでに時間がかかるものです。月次の集計でしか販売実績が見えない仕組みでは、需要が変わってから気づくまでに平均で半月ほどの情報の遅れが入ります。物理的な遅れは投資や工程改善で短縮できますが、情報の遅れは仕組みの設計だけで短縮できることが多く、費用対効果の高い打ち手になりやすい部分です。

遅れと均衡ループの組み合わせは、この手法が扱う現象の中で最も再現性が高いものです。教育の場では、卸と小売と工場が在庫を融通し合う卓上の演習として繰り返し使われてきました。参加者は誰も悪意なく、それぞれの持ち場で合理的に発注しているにもかかわらず、上流に行くほど発注量が激しく振れます。演習後に参加者へ原因を尋ねると、多くの場合は他の参加者の判断が悪かったという答えが返ってきます。しかし同じ結果が参加者を変えても再現するという事実が、原因が人ではなく構造の側にあることを示しています。次の節では、この現象を最小限のコードで再現します。

在庫調整のモデル、需要が20パーセント増えたときに発注はどう動くか

用意したのは、ストックが2つだけの極小のモデルです。1つ目のストックは手元の在庫です。2つ目のストックは、発注済みでまだ届いていない量、いわゆる輸送中の在庫です。フローは、入荷、出荷、そして発注の3つです。意思決定の規則はたった1行で、「今週の需要と同じだけ発注し、それに加えて、目標在庫と実在庫の差を調整率の分だけ埋める」というものです。

調整率は、在庫のずれをどれだけの速さで埋めにいくかを表します。0.10であれば、ずれの1割だけを今週の発注に上乗せします。1.00であれば、ずれの全額を今週1回で取り返そうとします。現場の感覚で言えば、調整率を上げることは「在庫のずれを早く元に戻せ」という指示に相当します。この指示の強さだけを変えて、他は一切変えずに、発注量の動きを比べます。

需要は第5週に100から120へ、1度だけ階段状に上がります。増加幅は20パーセントで、現実の需要変動としては穏やかな部類です。リードタイムは2週間、目標在庫は需要の3週間分としました。以下がコードの全文です。なお週の番号は、出力に合わせて第0週から数えます。

import numpy as np

def run(alpha, weeks=40, lead=2, base=100, step_at=5, step_to=120):
    """在庫のストックとフロー。alpha は在庫ギャップを埋める速さ(在庫調整率)。"""
    inv = base * 3.0                      # 在庫(ストック)
    pipeline = [base] * lead              # 発注済みで未着の分(もう1つのストック)
    orders, invs = [], []
    for t in range(weeks):
        demand = base if t < step_at else step_to
        target = demand * 3.0             # 目標在庫は需要の3週間分
        arrival = pipeline.pop(0)
        inv += arrival - demand           # ストックの増減=流入-流出
        order = max(0.0, demand + alpha * (target - inv))
        pipeline.append(order)
        orders.append(order)
        invs.append(inv)
    return np.array(orders), np.array(invs)

print("需要は第5週に100から120へ、20%だけ増える。発注がどう動くかを見る。")
print("%-10s %-14s %-14s %-16s" % ("調整率", "発注のピーク", "需要比の増幅率", "在庫の底"))
for alpha in (0.1, 0.25, 0.5, 1.0):
    o, i = run(alpha)
    print("%-10.2f %-14.1f %-14.2f %-16.1f" % (alpha, o.max(), o.max() / 120, i.min()))

for alpha in (0.1, 0.25, 0.5, 1.0):
    o, i = run(alpha)
    hit = np.flatnonzero(np.abs(i[10:] - 360) < 3.6)   # 目標の1%以内に入った週
    if hit.size:
        print("調整率%.2f: 在庫が目標360の1%%以内に戻るのは第%d週" % (alpha, hit[0] + 10))
    else:
        print("調整率%.2f: 40週たっても目標360の1%%以内に入らない(振動が続く)" % alpha)

コードの中心は inv += arrival - demand の1行です。ストックは流入から流出を引いた分だけ増える、という関係をそのまま書いただけです。この手法の数式は、突き詰めればこの形の積み上げしかありません。難しい数学ではなく、どの量をストックと見なし、どのフローがそれを動かすかという構造の設計に、この手法の本質があります。

実行結果は次のとおりです。

需要は第5週に100から120へ、20%だけ増える。発注がどう動くかを見る。
調整率        発注のピーク         需要比の増幅率        在庫の底
0.10       130.0          1.08           260.0
0.25       145.0          1.21           260.0
0.50       170.0          1.42           260.0
1.00       220.0          1.83           260.0
調整率0.10: 在庫が目標360の1%以内に戻るのは第35週
調整率0.25: 在庫が目標360の1%以内に戻るのは第14週
調整率0.50: 在庫が目標360の1%以内に戻るのは第12週
調整率1.00: 40週たっても目標360の1%以内に入らない(振動が続く)

結果の読み取り、急いで戻そうとするほど発注が振れ、やがて収まらなくなる

まず発注のピークを見ます。需要は120までしか上がっていないのに、発注は調整率0.10で130、0.25で145、0.50で170、1.00で220に達しています。需要比で言えば1.08倍から1.83倍です。需要が20パーセント増えただけの入力に対して、出力である発注が83パーセント増えるところまで膨らみます。この増幅が、上流の工場や部品メーカーには「需要が急増した」という信号として届きます。

この現象がブルウィップ効果です。鞭の柄をわずかに振ると先端が大きくしなる様子から名づけられており、サプライチェーンの下流で起きた小さな需要変動が、上流に遡るほど大きな変動になって現れることを指します。フォレスターの初期の研究で扱われた現象でもあるため、フォレスター効果と呼ばれることもあります。ここで作ったモデルは1階層しかありませんが、それでも増幅が起きます。階層が増えれば、各段で同じ増幅が掛け算されていきます。

ピークの値には規則性があります。130、145、170、220という数字は、いずれも 120 + 100 × 調整率 になっています。これは偶然ではありません。需要が上がった直後、目標在庫は300から360へ一段上がる一方、実在庫は260まで落ちています。差は100です。その差の何割を今週の発注で取りにいくかが調整率ですから、上乗せ額はそのまま100の何割かになります。調整率を1.00にするということは、100のずれを1週間で全部埋めにいくということです。

次に収束の速さを見ます。在庫が目標の360に対して1パーセント以内、つまり誤差3.6以内に入る最初の週は、調整率0.10で第35週、0.25で第14週、0.50で第12週です。ゆっくり調整すると、需要が動いてから30週間ほど在庫が目標から外れたままになります。速く調整すれば、ずれは早く解消します。この3つを並べる限りでは、調整率を上げるほど収束が速くなるという素直な傾向が読み取れます。速さと振れが逆を向いている、というのがここまでの読み取りです。

調整率発注のピーク需要比の増幅率在庫の底目標に戻る週
0.10130.01.08倍260.0第35週
0.25145.01.21倍260.0第14週
0.50170.01.42倍260.0第12週
1.00220.01.83倍260.040週たっても戻らない

ところが調整率1.00の行だけは、他の3つと性質が違います。40週を走らせても、在庫は一度も目標の1パーセント以内に入りません。0.50よりさらに速い調整を選んだのに、収束が速くなるどころか、収束そのものが起きなくなっています。0.10から0.50までで見えていた「上げれば速くなる」という傾向を、そのまま先へ延長してはいけない、ということです。

在庫の値を追うと、何が起きているかがはっきりします。調整率1.00では、在庫は260、340、440、460、380、280という6週の周期をひたすら繰り返します。40週でも400週でも同じで、いつまでたっても360に落ち着きません。発注も同様に、220、140、40、20、100、200という値を反復します。系は、外から新しい刺激を受けていないにもかかわらず、自分自身の調整だけで永久に揺れ続ける状態に入っています。

需要は第5週以降、最後までずっと120で一定です。それにもかかわらず、発注は220まで膨らんだ後、40や20まで落ち込みます。振れ幅は11倍です。上流の工場から見れば、注文が2倍近くに増えた直後に6分の1へ消え、しばらくするとまた押し寄せる、という信号が届くことになります。増産の準備をした矢先に仕事が無くなり、体制を縮小した頃に注文が戻ってきます。上流の担当者がどれほど優秀でも、この信号から、もとの安定した需要120を復元することはできません。ブルウィップ効果が最も極端な形で現れたのが、この振動です。

ここから、本章の中心的な主張が出てきます。速さと振れのトレードオフは、調整率が穏やかな範囲にあるときにだけ成り立つ話です。0.10は遅いが収束する。0.50は速くて収束する。1.00は収束しない。ある水準を越えて調整を強めると、トレードオフの土俵そのものから降りてしまい、系が安定しなくなります。「もっと速く在庫を戻せ」という現場への指示は、その水準を越えたところで、望んだ結果と正反対のものを生みます。遅れのある系において、調整の強さは、強ければ強いほど良いという性質を持っていません。

この転換点の位置は、事前に見当をつけられるものではありません。今回のモデルでは、リードタイムが2週間で、目標在庫が需要の3週間分で、発注規則が輸送中の在庫を見ていない、という条件の組み合わせが、1.00を不安定な側に置いています。条件が変われば境目も変わります。したがって実務では、採用しようとしている調整の強さが安定な側にあるかどうかを、その系の条件で確かめるほかありません。手元のモデルを何通りかの設定で走らせて、挙動が質的に変わる境目を探す作業には、この確認としての意味があります。

この読み取りには、シミュレーションの扱い方としての教訓も含まれています。もし収束までの週数を0.10から0.50の範囲でしか計算していなければ、「上げるほど速い」という傾向だけを見て、1.00での質的な変化を見落とします。要約された1つの数字は傾向を示しますが、その傾向が成り立つ範囲までは教えてくれません。時系列そのものを目で追い、想定する範囲の端を確かめる手続きが要ります。第10章で扱う検証と妥当性確認では、この種の確認を手続きとして組み込みます。

調整率ごとの発注量の推移を重ねた折れ線グラフの概念図

在庫の底が動かないこと、打ち手は係数ではなく構造にある

結果の表で、最も注目すべきなのは「在庫の底」の列です。調整率を0.10から1.00まで10倍に変えても、在庫の底は260.0のまま1ミリも動いていません。到達する週も、いずれの調整率でも第6週で同じです。調整率という運用パラメータをどれだけ強く握っても、在庫の落ち込みそのものは1つも防げていない、ということです。

理由は構造の側にあります。需要が第5週に120へ上がったとき、その週と翌週に届く荷物は、すでに2週間前に発注されたものです。当時の需要は100でしたから、届くのは100です。需要が120で入荷が100ですから、毎週20ずつ在庫が削られます。リードタイムが2週間なので、この状態が2週間続き、合計で40だけ在庫が減ります。300から40を引いて260。これが底の値です。調整率は発注の大きさを決めますが、その発注の効果が現れるのはリードタイムの後です。底が打たれるまでの2週間に、調整率が介入できる余地はありません。

言い換えると、この系で在庫の落ち込みを浅くする方法は、調整率の中には存在しません。リードタイムを2週間から1週間に縮めるか、需要の変化を実際に起きるより早く知るか、目標在庫の持ち方そのものを変えるか。いずれも構造を変える打ち手です。運用の努力ではなく設計の変更です。

もう1つ、累積の発注量にも同じ性質があります。需要が上がった第5週以降について、発注量から120を引いた値を全期間にわたって足し合わせると、調整率0.10でも0.25でも0.50でも、合計はきっちり100になります。目標在庫が60増え、輸送中の在庫が40増えるので、余分に流し込むべき量は必ず100だからです。調整率が決めているのは、この100をいつ出すかという配分だけです。速く出せば背が高く尖った山になり、ゆっくり出せば低くて長い丘になります。面積は一定で、形だけが変わります。

ここから導かれる結論は明快です。速さと振れは、同じ100の配分の仕方であって、両立しません。振れを小さくする調整率は在庫の回復を遅くし、回復を速くする調整率は振れを大きくします。どちらか一方だけを良くする調整率は存在しません。したがって「発注の振れも抑えつつ在庫も早く戻せ」という指示は、この構造のもとでは実行不可能な要求です。実行可能にするには、指示ではなく構造を変える必要があります。しかも、調整率1.00で見たように、配分を極端に前倒しすると、山が高くなるだけでは済みません。系が振動に入り、そもそも配分が終わらなくなります。トレードオフの中でどこを選ぶかという問題は、安定な範囲に留まっている限りでの問題です。

打ち手どこを変えるかこの構造への効き方
調整率を下げる運用パラメータ振れは小さくなるが、在庫の回復が遅くなる。底は変わらない
調整率を上げる運用パラメータ回復は速くなるが、振れが大きくなる。強すぎると収束しなくなる
リードタイムを縮める構造在庫の底が浅くなり、埋めるべき量そのものが減る
需要情報を上流と共有する構造上流が発注量ではなく実需要を見るため、増幅の連鎖が切れる
発注ロットを小さくする構造まとめ発注による人工的な変動が減る
輸送中の在庫を計算に入れる構造発注済みの分を二重に取りにいく行き過ぎが減る

表の最後の行は、このモデルには入っていない改良です。今回の発注規則は手元の在庫だけを見ており、すでに発注して輸送中の分を考慮していません。そのため、発注した直後にもう一度同じずれを見て、また発注してしまいます。輸送中の在庫を差し引いてから発注量を決める規則に変えると、振れは大幅に減ります。この改良は調整率をいじるのではなく、意思決定が参照する情報を変えるものであり、やはり構造側の打ち手です。この手法が経営に対して提供する視点は、この「係数ではなく構造を見る」という一点に集約されます。

因果ループ図とストック・フロー図、数値の前に合意を作る

この手法には、2つの表記法があります。1つは因果ループ図で、要素を線で結び、矢印の先に「同じ方向に動く」なら正、「逆方向に動く」なら負の符号を添えます。輪が閉じたときに負の符号が偶数個なら自己強化ループ、奇数個なら均衡ループになります。遅れがある箇所には、矢印の途中に二重線を引いて印をつけます。

もう1つはストック・フロー図で、ストックを四角、フローを蛇口の付いた矢印、系の外側を雲で表します。こちらは数値計算に直結する表記で、図がそのままモデルの式に対応します。今回のコードで言えば、在庫と輸送中の在庫が四角、入荷と出荷と発注が蛇口付きの矢印にあたります。

実務で先に使う価値が高いのは、因果ループ図の方です。理由は、数値が1つも無い段階で描けるからです。関係者を集めて、「何が何に影響するか」を矢印で描き、符号を付け、遅れの箇所に印を入れる。この作業だけで、認識のずれが表面化します。ある部門が「品質を上げれば問い合わせが減る」という輪を想定している一方で、別の部門が「品質を上げると要求水準が上がって問い合わせが増える」という輪を想定していることが分かる、といった具合です。どちらが正しいかを決める前に、両方の輪が同じ図の上に載ることに意味があります。

因果ループ図を描くときに、実際に起きやすい失敗が2つあります。1つは、要素を増やしすぎて図が読めなくなることです。輪が10本を超えると、人間は符号を追えなくなります。主要な輪を3本から5本に絞り、残りは別の図に分けるのが現実的です。もう1つは、要素の名前に方向を含めてしまうことです。「在庫不足」という名前を付けると、それが増えるとは在庫が減ることなのか不足感が強まることなのかが曖昧になり、符号が定まりません。「在庫量」のように、増減の方向を含まない中立な名詞にしておくと、符号の議論が安定します。

因果ループ図の限界も理解しておく必要があります。図は構造を表しますが、挙動は表しません。同じ図から、収束する挙動も、振動する挙動も、発散する挙動も出てきます。どれが起きるかは、遅れの長さや係数の大きさで決まり、それは図を眺めても分かりません。今回のモデルが良い例で、図の上では調整率0.10も1.00もまったく同じ形をしていますが、一方は収束し、もう一方は永久に振動します。合意を作るところまでは図で進め、挙動を知りたい段階でストック・フロー図に落として数値を入れる、という順序になります。

使いどころと限界、平均の流れを見る道具として

この手法が力を発揮するのは、対象が集計量として扱え、フィードバックと遅れが挙動を支配している場面です。一般論として、次のような領域が該当します。

  • サプライチェーンの需給変動。発注、在庫、輸送のリードタイムが作る増幅と振動の分析
  • 人員計画。採用から戦力化までの育成期間と、負荷の増大が離職を招く輪の相互作用
  • 設備投資と稼働率のサイクル。需要増に反応して増設した設備が、稼働を始める頃には需要が落ち着いている、という型の周期
  • 市場シェア争い。自己強化ループの主導権争いと、先行者が持つ蓄積の効き方
  • 制度や政策の中長期効果。効果が出るまでの遅れが長く、途中経過だけを見ると失敗に見える種類の施策
  • カスタマーサポートの負荷と品質。処理が追いつかないと品質が落ち、品質が落ちると再問い合わせが増えて負荷がさらに増える悪循環
  • 技術的負債。負債が増えると開発速度が落ち、速度が落ちると場当たり的な対応が増えて負債がさらに増える輪

共通しているのは、時間軸が長いこと、当事者が対策を打つこと、そしてその対策の効果が遅れて現れることです。この3つが揃うと、直感的な判断がしばしば裏目に出ます。逆に言えば、遅れが無視できるほど短い問題や、フィードバックが存在せず一方向に流れるだけの問題には、この手法を持ち出す必要はありません。

限界も明確です。集計量で扱う以上、個体差を表現できません。誰が待っているのか、その人が何分待ったのか、待ち時間の分布の裾はどうなっているのかを問うことはできません。今回のモデルには「在庫」という平均的な量しかなく、特定のロットや特定の顧客という概念そのものが存在しません。窓口を1つ増やしたときに待ち時間の95パーセント点がどう動くかは、第4章と第5章で扱った離散イベントの道具でなければ答えられません。

また、確率的なばらつきも標準的な形では扱いません。今回のモデルの需要は、階段状にきれいに上がるだけで、週ごとのぶれがありません。現実の需要はぶれます。ぶれを入れたい場合はモンテカルロと組み合わせることになりますが、その場合でも扱えるのは集計量のばらつきであって、個体ごとの経験ではありません。手法を選ぶ基準は、問いの粒度にあります。

問いの形適した道具本コラムの該当章
誰が何分待つか、待ち時間の分布はどうなるか離散イベントシミュレーション第4章、第5章
個々の判断や関係の違いから全体が何に落ち着くかエージェントベースモデリング第6章
系全体の量が時間とともにどう振れるかシステムダイナミクス本章
不確実性を織り込んだときに結果はどう散らばるかモンテカルロ法第3章

実務では、これらを排他的に選ぶ必要はありません。同じ事業に対して、四半期単位の需給の振れをこの手法で見て、日次の工程の詰まりを離散イベントで見る、という併用は自然な形です。粒度の違う2つのモデルを同時に持つことは、矛盾ではなく分業です。避けるべきなのは、1つのモデルにすべての問いを答えさせようとして、モデルが肥大化し、誰も検証できない状態になることです。第2章で述べた「問いがモデルの型を決める」という原則は、ここでも同じように働きます。

最後に、本章で確認したことを整理します。ストックとフローを分けて考えると、議論の対象がはっきりします。フィードバックループと遅れが組み合わさると、個々の判断が合理的でも系は振動します。在庫調整のモデルでは、需要が20パーセント増えただけで発注が最大1.83倍まで膨らみ、調整率を上げるほど振れが大きくなり、1.00では収束すらしなくなりました。そして在庫の底260は、調整率をどう変えても動きませんでした。運用の努力で変えられる範囲と、構造を変えなければ動かない範囲は、はっきり分かれています。どちらの領域の問題なのかを見分けることが、この手法を業務に持ち込むときの最初の仕事になります。

この章を深めたい方への参考書籍

シミュレーションによるシステムダイナミックス入門(土金達男、東京電機大学出版局)
ストックとフローでモデルを組む手順を、実際に動かしながら学べる本です。本章の在庫の例を、自分の業務の構造に置き換えるときの足がかりになります。

システム思考をはじめてみよう(ドネラ・H・メドウズ 著、枝廣淳子 訳、英治出版)
フィードバックループという見方を、平易な言葉で説明した薄い本です。数式に入る前に、構造で考えるとはどういうことかを掴めます。

第8章 入力のモデリング、データから分布を決める

ここまでの章では、モデルの型と動かし方を扱ってきました。本章が扱うのは、そのモデルに何を入れるかです。到着の間隔、作業にかかる時間、故障までの時間、需要の量。こうした量をどんな確率分布として表現するかを決める工程を入力モデリングと呼びます。地味な工程ですが、シミュレーションの結果を左右する度合いでいえば、モデルの構造を作り込むことよりも大きい場面が少なくありません。

実務でこの工程が軽く扱われる理由は分かりやすいものです。現場に「1件あたり何分かかりますか」と聞けば「だいたい10分です」という答えが返ってきます。この10分をモデルに入れれば動きます。動いて数字が出るので、それで終わりにしてしまう。ところが、この「だいたい10分」という一言には、10分ぴったりで終わる作業なのか、3分で終わることも30分かかることもある作業なのか、という決定的な違いが含まれていません。そして待ちや在庫や納期といった指標では、この違いが結果を何倍も変えます。

本章の中心は、その差を数値で確かめる実験です。平均サービス時間をすべて1.0分に揃えたまま、ばらつきの形だけを3通りに変えて、平均待ち時間がどう変わるかを測ります。結論を先に書くと、平均が同じでも待ち時間は3倍以上変わります。この事実を押さえたうえで、データから分布を決める手順、よく使う分布とその選び方、当てはまりの確かめ方、現場データに潜む落とし穴、データが無いときの進め方、そして乱数の種の管理までを順に扱います。

平均だけを入れたモデルは、平均的な答えを出さない

まず、なぜ平均値だけでは足りないのかを整理します。ある入力 \( X \) に対して出力が \( f(X) \) で決まるとき、\( f \) が直線であれば \( E[f(X)] = f(E[X]) \) が成り立ちます。平均を入れれば平均が出てきます。ところが \( f \) が曲がっていると、この等式は崩れます。曲がり方が上に凸か下に凸かで、平均を入れた答えは真の平均より小さくなったり大きくなったりします。これはイェンセンの不等式として知られている性質です。

待ち時間はまさにこの「曲がった関数」の代表例です。第5章で見たとおり、平均待ち時間には \( \dfrac{\rho}{1-\rho} \) という項が入ります。利用率が上がるほど急激に立ち上がる形です。利用率そのものが日によって変動するなら、変動を無視して平均の利用率だけを入れた計算は、待ち時間を過小に見積もります。混んだ日の待ちの増え方が、空いた日の待ちの減り方より大きいからです。

在庫でも同じことが起きます。需要が平均100個の月に100個だけ用意しておけば足りる、という計算は成り立ちません。需要が120個の月には20個の欠品が出て、80個の月には20個の余剰が出ます。欠品の損失と余剰の損失は普通は同額ではありませんし、そもそも欠品が出た月の売り逃しは、余剰が出た月に取り返せません。ばらつきがある限り、平均で設計した在庫は平均的な成績を出しません。

納期についても同様です。5つの工程がそれぞれ平均2日で終わるとして、合計の平均は10日です。ところが「10日以内に終わる確率」を問うと、答えは50パーセントではありません。工程が直列でつながっていて、どこか1つでも遅れれば全体が遅れる構造では、遅れの影響が積み上がります。平均の合計は計算できても、確率の話は分布を入れないと出てきません。

したがって、入力を平均値の1点で置くことは、モデルを単純化しているのではなく、答えを別のものに変えてしまっていることになります。この点は、モデルの前提として明示的に共有しておく必要があります。時間がなくて平均だけで組んだのであれば、その旨を報告書に書く。書かずに数字だけを渡すと、受け取った側はばらつきを織り込んだ答えだと思って読みます。

ばらつきの形だけを変えると、待ち時間は3倍以上変わる

実際に数値で確かめます。その前に、ここから繰り返し出てくる変動係数という言葉を確認しておきます。標準偏差を平均で割った値のことで、ばらつきの大きさを平均に対する比率で表したものです。第3章で触れたとおり、平均10分の作業が前後3分ばらつくのと、平均100分の作業が前後3分ばらつくのとでは、同じ3分でも意味が違います。変動係数は、その違いを吸収して比べられるようにした指標です。値が0ならばらつきが無く、1なら標準偏差が平均と同じ大きさ、1.5ならさらに大きく散らばっている、という読み方をします。

実験は次のように組みます。窓口が1つの単純な系を用意し、到着は1分あたり0.8件のポアソン過程で固定します。サービス時間の平均も1.0分で固定します。変えるのはサービス時間の分布の形だけです。ばらつきが無い「一定」、変動係数が1の「指数分布」、変動係数が1.5の「対数正規分布」の3通りを比べます。一定はどの件も同じ時間で終わる状態、指数分布は短時間で終わる件が多く時々長くかかる形、対数正規分布はそれよりさらに右へ長く裾を引く形です。それぞれの分布の性質は、この節のあとの表であらためて整理します。利用率はどの条件でも \( \rho = 0.8 \) で同じです。

import numpy as np
import simpy

LAM, MEAN_SERVICE, N_REP, SIM = 0.8, 1.0, 20, 100_000

def run(kind, seed):
    rng = np.random.default_rng(seed)
    env = simpy.Environment()
    server = simpy.Resource(env, capacity=1)
    waits = []

    def service_time():
        if kind == "指数分布":
            return rng.exponential(MEAN_SERVICE)
        if kind == "一定":
            return MEAN_SERVICE
        if kind == "対数正規(CV=1.5)":
            sigma = np.sqrt(np.log(1 + 1.5 ** 2))
            return rng.lognormal(np.log(MEAN_SERVICE) - sigma ** 2 / 2, sigma)
        raise ValueError(kind)

    def customer(env):
        t0 = env.now
        with server.request() as req:
            yield req
            waits.append(env.now - t0)
            yield env.timeout(service_time())

    def source(env):
        while True:
            yield env.timeout(rng.exponential(1 / LAM))
            env.process(customer(env))

    env.process(source(env))
    env.run(until=SIM)
    return np.mean(waits[1000:])

kinds = ("一定", "指数分布", "対数正規(CV=1.5)")
means = {k: np.mean([run(k, s) for s in range(N_REP)]) for k in kinds}

print("平均サービス時間はどれも1.0分。ばらつきの形だけを変える。")
print("%-18s %-8s %-16s %-12s" % ("サービス時間の分布", "変動係数", "平均待ち時間(分)", "一定の場合の何倍"))
cv = {"一定": 0.0, "指数分布": 1.0, "対数正規(CV=1.5)": 1.5}
for k in kinds:
    print("%-18s %-8.1f %-16.3f %-12.2f" % (k, cv[k], means[k], means[k] / means["一定"]))

コードの作りを補足します。1条件につき種を0から19まで変えて20回まわし、その平均を取っています。1回のまわす長さは100,000分で、最初の1000人ぶんの待ち時間は捨てています。開店直後は列が空なので待ち時間が不自然に短く出るためで、この立ち上がりの扱いは第9章で改めて扱います。対数正規分布のパラメータは、平均が1.0、変動係数が1.5になるように逆算して与えています。実行結果は次のとおりです。

平均サービス時間はどれも1.0分。ばらつきの形だけを変える。
サービス時間の分布          変動係数     平均待ち時間(分)        一定の場合の何倍
一定                 0.0      2.015            1.00
指数分布               1.0      3.943            1.96
対数正規(CV=1.5)       1.5      6.386            3.17

3行とも平均サービス時間は1.0分で、到着の条件も同じです。違うのは変動係数の列だけで、0.0、1.0、1.5と並んでいます。それに対応して平均待ち時間は2.015分、3.943分、6.386分となり、右端の列が示すとおり1.00倍、1.96倍、3.17倍に開きます。現場に「1件あたり平均1分です」とだけ聞いてモデルを作った場合、その1分がどのくらいばらつくかによって、答えは3倍以上ずれる可能性があるということです。窓口を増やすかどうかの判断を、この3倍の幅の中でしていることになります。

変動係数と倍率がこの並びになる理由は、第5章のキングマンの近似式で説明できます。式は \( W_q \approx \dfrac{\rho}{1-\rho} \cdot \dfrac{C_a^2 + C_s^2}{2} \cdot E[S] \) でした。この実験では到着も利用率も平均サービス時間も固定しているので、右辺のうち動くのは真ん中の \( \dfrac{C_a^2 + C_s^2}{2} \) の項だけです。到着がポアソンなので \( C_a = 1 \) は固定で、サービス時間の変動係数 \( C_s \) が0.0、1.0、1.5と上がると、この項は0.5、1.0、1.625と変わります。0.5を1とみなした比は1.00、2.00、3.25です。実測の倍率1.00、1.96、3.17は、この比をほぼそのままなぞっています。ここで効いているのが変動係数の2乗である点は見落とせません。変動係数が1.0から1.5へ、1.5倍になっただけで、真ん中の項は1.0から1.625へと1.6倍以上になります。ばらつきが少し大きい程度に見えても、待ち時間への影響はその比では済みません。

絶対値でも確認できます。利用率0.8なので \( \dfrac{\rho}{1-\rho} = 4 \)、平均サービス時間 \( E[S] = 1 \) ですから、予測値は一定のとき \( 4 \times 0.5 \times 1 = 2.0 \) 分、指数分布のとき4.0分、変動係数1.5のとき6.5分になります。実測はそれぞれ2.015分、3.943分、6.386分で、いずれも予測と数パーセント以内で一致しています。差は有限の長さで打ち切ったことによる誤差です。到着がポアソンの場合、この式は近似ではなく厳密な値を与えることが知られており、実測が理論に寄っていることでモデルの実装が正しいことも同時に確認できています。第10章で扱う検証の実務では、こうした突き合わせが最初の関門になります。

経営の言葉に直すと、こうなります。待ち時間を減らしたいときの打ち手は、能力を増やすことだけではありません。処理時間の平均を1分のまま変えずに、ばらつきだけを変動係数1.5から0にできれば、待ち時間は6.386分から2.015分へ、7割近く減ります。設備も人員も増やしていません。逆に言えば、ばらつきの実態を把握しないままモデルを作ると、この打ち手の効果が評価対象から丸ごと抜け落ちます。

平均が同じで形が違う3つのサービス時間分布を左に並べ(一定は縦棒1本

入力モデリングの6つの段階

入力の分布を決める作業は、次の6段階で進めるのが標準的な流れです。順序に意味があるので、飛ばさずに追う価値があります。

  1. 何を確率変数として扱うかを決める。すべてをばらつかせる必要はありません
  2. データを集める。どの系から、どの期間、どの粒度で取るかを決めます
  3. 形を見る。ヒストグラム、時間帯別、曜日別、品目別に分けて眺めます
  4. 候補の分布を当てはめる。形の見当がついたものだけを候補にします
  5. 当てはまりを確かめる。図と検定の両方で見ます
  6. 合わないなら経験分布を使う。無理に型にはめません

第1段階の「何を確率変数にするか」は、意外に軽視されます。モデルに登場する量をすべて分布にすると、推定すべきパラメータが増え、データの要求量が跳ね上がり、結果の解釈も難しくなります。判断の基準は単純で、その量のばらつきが答えに効くかどうかです。作業時間の10パーセントを占める工程のばらつきは、たいていの場合、答えを変えません。逆に、能力の制約になっている工程のばらつきは必ず効きます。第11章で扱う実験計画の考え方を使って、効く入力を先に絞り込むこともできます。

第2段階のデータ収集で決めるべきことは、期間と粒度です。期間は、季節性が1周する長さを含んでいるかを見ます。小売なら1年、コールセンターなら少なくとも数か月は要ります。粒度は、モデルの時間刻みより細かいことが条件です。分単位で動くモデルに日次の集計データを渡しても、ばらつきの情報は失われています。この段階で「もっと細かいログが取れないか」を情報システム部門に確認しておくと、後の手戻りが減ります。

第3段階で形を見るときは、必ず層に分けて見ます。全データを1枚のヒストグラムにすると、山が2つある分布が1つのなだらかな山に見えたり、逆に単なる測定粒度の影響が本質的な構造に見えたりします。時間帯別、曜日別、品目別、担当者別に分けて描き、層ごとに形が違うなら、そこにモデル化すべき構造があります。層で説明できるばらつきを分布のばらつきとして押し込めると、モデルの説明力が落ちます。

第4段階の当てはめは、多くの場合、最尤法でパラメータを推定します。統計ソフトやPythonのライブラリが用意している関数で済みます。ここで気をつけるのは、候補を絞ってから当てはめることです。手当たり次第に20種類の分布を当てはめて一番よく合ったものを選ぶと、たまたま合っただけのものが選ばれます。第5段階と第6段階は、それぞれ節を改めて扱います。

よく使う分布と、それが自然に出てくる場面

分布の選択は、データの形だけで決めるものではありません。その量がどういう仕組みで生まれているかを考えると、自然な候補が絞れます。理屈から候補を出し、データで確認する。この順序であれば、データが少ないときにも判断ができます。

分布自然に出てくる場面なぜその形になるか必要なもの
指数分布互いに独立な到着の間隔、無記憶な故障の間隔多数の独立した相手が、それぞれ小さな確率で行動を起こすとき、次の1件までの間隔はこの形になります。過去にどれだけ待ったかが次に影響しません平均だけ
ポアソン分布一定時間内に届く件数、1日の来店客数間隔が指数分布に従うときの、区間内の件数の分布です。平均と分散が等しくなる性質があります平均だけ
対数正規分布作業時間、処理時間、修理時間、案件金額複数の要因が足し算ではなく掛け算で効くとき、この形になります。負の値を取らず、右に長い裾を持ちます平均と変動係数
正規分布寸法の誤差、多数の小さな要因の合計独立した多数の要因が足し算で効くとき、中心極限定理によりこの形に近づきます。負の値を取りうるので時間には向きません平均と標準偏差
三角分布データが無く、最小値と最頻値と最大値の3点しか分からないとき理論的な裏付けではなく、3点の見積もりから素直に作れる形として使います3点の見積もり
一様分布範囲しか分からないとき、乱数の土台範囲の中でどこも同じ確からしさ、という仮定を置いた形です。情報が最も少ない状態を表します下限と上限
ワイブル分布故障までの時間、部品の寿命故障率が時間とともに変わる状況を表せます。形状パラメータで初期故障型と摩耗故障型を書き分けられます形状と尺度
経験分布データはあるが、どの型にも当てはまらないとき観測値そのものを分布として使います。山が2つある場合や、極端な裾がある場合に有効です十分な量のデータ

この表で実務上いちばん出番が多いのは、作業時間に対する対数正規分布です。人が関わる作業の時間は、標準的な手順どおりに進めば短く終わり、何か引っかかると倍以上かかる、という非対称な形になりがちです。左側は0分より短くなれないので詰まっており、右側だけが伸びます。正規分布を当てはめると、この非対称性が表現できないうえに、平均に対して標準偏差が大きいときには負の時間を生成してしまいます。

到着間隔に指数分布を使うのは、来る人どうしが相談せずにばらばらに来る場合です。この仮定が崩れるのが、団体客やバスの到着、始業時刻に合わせた集中、通知メールを送った直後のアクセスです。こうした場合は、到着の件数そのものをまとまりとして扱う必要があります。1回の到着で複数件が同時に入るモデルにするか、時間帯ごとに到着率を変えるかのどちらかで対応します。

ワイブル分布は保全の検討で使います。形状パラメータが1より小さいと故障率が時間とともに下がる初期故障型、1のときは一定で指数分布と一致し、1より大きいと故障率が上がる摩耗故障型になります。予防保全の間隔を決める検討では、摩耗故障型かどうかで結論が変わります。故障率が一定なら、部品を新品に替えても故障確率は下がらないので、予防交換に意味がありません。

三角分布は、データが無い状況での実務的な逃げ道です。現場の担当者に「最短で何分、いちばん多いのは何分、最長で何分」の3点を聞けば作れます。平均は3点の単純平均になります。理論的な裏付けは無いので、正しい分布だと主張するものではありません。仮定を明示して幅を持たせるための道具として使います。

当てはまりの確かめ方、検定の合否だけで決めない

候補の分布を当てはめたら、その当てはまりを確認します。適合度検定は、観測されたデータが仮定した分布から生じたと考えて矛盾がないかを、統計的に判定する手続きです。代表的なものにコルモゴロフ・スミルノフ検定とカイ二乗検定があります。前者は累積分布の縦方向の最大のずれを見るもの、後者はデータを区間に分けて、各区間の観測度数と期待度数を比べるものです。

ただし、検定の合否だけで分布を決めるのは危険です。理由は検定の性質そのものにあります。これらの検定は「仮定した分布と一致する」という帰無仮説を棄却するかどうかを判定します。データが増えるほど検出力が上がるので、わずかなずれでも棄却されます。現実のデータが数学的にきれいな分布と完全に一致することはまずないので、数万件のログを使えば、どの分布を当てはめてもほぼ確実に棄却されます。

逆に、データが数十件しかないときは、どの分布を当てはめても棄却されません。検出力が足りないからです。つまり、この検定は「データが少ないと何でも通り、データが多いと何も通らない」という挙動をします。合否をそのまま採否の基準にすると、判断がデータ量に引きずられます。

実務で重視すべきなのは、目的の指標にとって効く部分が合っているかどうかです。待ち時間を知りたいのであれば、サービス時間の分布の右側の裾、つまり長時間かかる案件の頻度が合っているかが決定的です。裾が実態より軽い分布を当てはめると、待ち時間は過小に出ます。逆に、分布の中央付近が多少ずれていても、待ち時間への影響は限定的です。何を答えたいかによって、どこを合わせるべきかが変わります。

確かめ方何が見えるか使いどころと限界
ヒストグラムと密度関数の重ね書き山の位置と数、全体の形最初に必ず見ます。区間の幅の取り方で印象が変わるので、幅を数通り変えて描きます
累積分布の重ね書き全体のずれ方、裾の合い方区間幅に依存しないので、ヒストグラムより安定して読めます
Q-Qプロット分位点どうしのずれ。特に裾のずれ直線から外れる位置で、どこが合っていないかが分かります。裾の確認に向きます
コルモゴロフ・スミルノフ検定累積分布の最大のずれが偶然の範囲かデータが多いとほぼ必ず棄却されます。参考値として見ます
カイ二乗検定区間ごとの度数のずれ区間の切り方で結果が変わります。件数の少ない区間はまとめる必要があります
目的の指標での比較候補の分布ごとに答えがどれだけ変わるか最終的な判断材料です。答えが変わらないなら、どちらを選んでも実害はありません

最後の行が実務では最も有効です。候補の分布が2つあって決めきれないとき、両方でシミュレーションを回して答えを比べます。窓口を1台増やすという結論が両方で同じなら、分布の選択に時間をかける理由はありません。結論が変わるなら、そこが投資してでもデータを取りに行くべき箇所だと分かります。分布の選択の重要度も、目的の指標に照らして測るのが筋です。

どの候補も合わないときは、経験分布を使います。観測されたデータそのものを並べ、そこから復元抽出する方法です。山が2つある分布、極端な裾を持つ分布、離散的な値しか取らない分布のいずれにも対応できます。欠点は、観測されたことのない値が絶対に生成されないことです。過去の最大値が180分なら、モデルの中で181分の作業は永遠に起きません。裾のリスクを評価したい検討では、この性質が問題になります。

現場データの落とし穴

入力モデリングで実際に時間を取られるのは、分布の選択よりもデータの前処理です。業務システムのログはシミュレーションのために設計されていないので、必要な項目が欠けていたり、別の意味の時間が混ざっていたりします。よくある落とし穴を整理します。

落とし穴どう現れるか対処
作業開始の打刻が無い受付時刻と完了時刻しか無いため、待ち時間と作業時間が分離できない。そのまま使うと作業時間が過大になる閑散時間帯のデータだけを使う。待ちがほぼ0の時間帯なら、受付から完了までがほぼ作業時間になります
中断と休憩が混ざる昼休みをまたいだ案件の作業時間が60分長く出る。分布の右の裾に不自然な山ができる稼働時間だけを数える時間軸に変換する。あるいは休憩をまたいだ記録を除外する
期間の偏り繁忙期だけのデータで作ると能力不足の結論になり、閑散期だけだと余裕がある結論になる1年を通した期間から取り、時期を層として分けて扱います
丸めによる階段化5分単位で入力させているため、ヒストグラムが5分刻みの棒の集まりになる。連続分布を当てはめると形が合わない丸めの幅より細かい議論をしない。あるいは丸めをモデル化して、連続分布に一様な誤差を足して再現します
打ち切り一定時間で強制終了された記録が「その時間ちょうど」に集まる。実際はもっと長かったはずの案件が短く記録される打ち切りがあることを前提にした推定を行う。信頼性工学で扱う打ち切りデータの手法が使えます
到着率の非定常性1日を通した平均の到着率を使うと、ピーク時間帯の混雑が再現されない時間帯ごとに到着率を変える。実務ではこれが定番です
外れ値の扱いシステム障害で1件だけ8時間かかった記録が、平均と分散を大きく動かす原因が特定できる異常は除外し、除外した事実を記録します。原因が分からないものは残します

最初の「作業開始の打刻が無い」は、実務で最も頻繁に遭遇します。受付時刻と完了時刻だけがある状態でその差を作業時間としてモデルに入れると、待ち時間を二重に数えることになります。モデルの中で待ちが発生し、そのうえで待ちを含んだ作業時間を消費するので、答えは大きく過大になります。この誤りは動作としては正常に見えるため、気づかれないまま結論まで進んでしまいます。

回避の定石は、閑散時間帯のデータだけを使うことです。列がほとんど発生していない時間帯であれば、受付から完了までの時間はほぼ作業時間そのものです。この方法には、閑散時間帯の作業が繁忙時間帯より丁寧で遅くなる可能性という別の偏りがありますが、待ち時間を作業時間に混ぜ込むよりは害が小さくなります。可能であれば、情報システム部門に作業開始の打刻を追加してもらうのが本筋です。

到着率の非定常性については、対処の方法が確立しています。1日を1時間ごとの区間に分け、区間ごとに到着率を推定して、その値を使う方法です。区間の中では一定の到着率とみなし、区間をまたぐときに率を切り替えます。曜日で傾向が違うなら、曜日と時間帯の組み合わせで表を作ります。月初と月末で違うなら、その軸も加えます。表が細かくなるほど各マスのデータが減るので、どこまで細かくするかはデータ量との兼ね合いになります。

丸めの問題は見落としやすいところです。担当者が手入力する記録では、5分単位や15分単位に丸められた値が並びます。ヒストグラムが階段状になっているのを見て「特殊な分布だ」と考えるのは誤りで、単に入力の粒度がそうなっているだけです。この場合、丸めの幅より細かい精度の議論はできません。作業時間の平均が12.3分なのか12.7分なのかを論じても、元データにその情報は入っていません。

業務ログの1行を左に置き

データが無いときの進め方

入力モデリングの相談で最も多いのが「データが無いのでシミュレーションはできない」という判断です。しかし、データが無いことは検討を止める理由にはなりません。データが無いという状態を、仮定を明示した幅として扱えばよいからです。むしろ、データが無い領域こそ、意思決定の前に何が起きうるかを試しておく価値があります。

第1の方法は現場観測です。ストップウォッチで数十件を測ります。数十件では精密な分布の推定はできませんが、平均の見当と、ばらつきがどの程度かの見当はつきます。本章の実験で見たとおり、待ち時間に効くのはばらつきの大きさなので、変動係数がおおよそ0.3なのか1.5なのかが分かるだけで、答えの精度は大きく変わります。観測の際は、何を作業時間の始点と終点にしたかを定義して記録します。

第2の方法は3点見積もりです。現場の担当者に、最短・最頻・最長の3点を聞き、三角分布に落とします。この方法の弱点は、聞かれた人が最長値を控えめに答える傾向があることです。「いちばん時間がかかったときで何分ですか」と聞くと、記憶に残っている範囲の答えが返ります。実際の裾はそれより長いことが多いので、最長値は聞いた値より広めに置いておくのが安全です。

第3の方法は類似工程からの借用です。同じ会社の別の拠点、同じ設備を使う別の製品、業界で公開されている標準時間などから持ってきます。借用したことと、借用元との違いを記録に残すことが条件です。借用元が自動化されていて借用先が手作業なら、ばらつきの大きさは違うはずです。平均を借りてもばらつきは借りられない、という場合もあります。

そして、いずれの方法を取る場合でも、幅を広めに置いて感度分析をします。平均が10分で変動係数が1.0という1点の仮定でモデルを回すのではなく、変動係数を0.5、1.0、1.5と振って、結論が変わるかどうかを見ます。3通りとも「窓口を2台にすべき」という結論になるなら、その結論はデータの不足に耐えます。1.5のときだけ3台必要になるなら、変動係数の実測が投資判断のために必要だと分かります。どこにデータ取得の労力を投じるべきかを、感度分析が教えてくれます。

報告の際は、仮定を表にして添えます。どの入力をどんな根拠で置いたか、根拠が観測なのか見積もりなのか借用なのか、感度分析で結論が変わったのはどれか。この表があると、後からデータが手に入ったときに、どこを差し替えれば結論が更新できるかが分かります。第16章で扱うモデル台帳の中核をなす情報でもあります。

乱数の種と入力どうしの関係、再現できる形で残す

入力の分布が決まったら、モデルはその分布から値を引きます。この「引く」役割を担うのが疑似乱数です。疑似乱数は、決められた計算手続きによって、統計的に無作為に見える数列を作る仕組みです。本当の無作為ではなく、初期値を与えれば同じ数列が再現されます。この初期値を種、あるいはシードと呼びます。本章のコードでは np.random.default_rng(seed) の形で種を明示的に渡しており、seed に0を渡せば何度実行しても同じ待ち時間の系列が得られます。20回の反復は、種を0から19まで変えることで実現しています。

種の管理は、実務では成果物の一部として扱う必要があります。会議で「案Aの平均待ち時間は8.2分でした」と報告した後で、数字の根拠を問われる場面は必ず来ます。そのとき、モデルのコードと入力データと種の3つが揃っていれば、まったく同じ8.2分が再現できます。種を記録していなければ、同じコードを回しても別の数字が出ます。8.2分ではなく8.5分が出たとき、モデルを直したのか乱数が違うだけなのかを切り分けられません。記録すべき項目は、種のほか、使ったライブラリとそのバージョン、1回のまわす長さ、反復回数、捨てた立ち上がり期間の長さです。乱数生成の実装はライブラリのバージョンで変わることがあるため、バージョンを控えていないと数年後に再現できません。

種を固定することには、もう1つ実務的な利点があります。案Aと案Bを比べるとき、両方に同じ種を使えば、両者は同じ需要の並びに対して評価されることになります。同じ日に同じ客が来た場合の比較になるので、差が案の違いによるものだと言いやすくなります。この考え方は第11章で扱う共通乱数法として整理されています。ただし、種を1つだけ固定して結論を出すのは誤りです。特定の種のもとで得られた結果は、たまたまその並びで起きたことにすぎません。種を変えて複数回まわし、結果のばらつきを見たうえで判断します。本章の実験で20回の反復を取っているのはこのためで、何回まわせば信じてよいのかという問いは第9章で正面から扱います。

入力モデリングの最後の論点が、入力どうしの関係です。ここまで扱ってきたのは、1つの量をどんな分布で表すかという話でした。実際のモデルでは複数の量を扱いますが、それぞれを独立に引いてよいとは限りません。独立でないものを独立として扱うと、分布の形が正しくても答えが外れます。典型的なのが、処理時間と品目の関係です。全案件を混ぜた1つの分布から引くと、簡単な案件と複雑な案件がランダムに混ざった系列が生成されます。ところが現実には、複雑な案件が特定の時間帯や特定の顧客層に偏っていることがあります。この偏りが待ち時間のピークを作っているのなら、混ぜた分布ではそのピークが再現されません。品目ごとに分布を分け、品目の到着そのものをモデル化する必要があります。

到着の集中も同様です。バスが着くと一斉に人が増える、電子メールを送った直後にアクセスが集中する、月末に伝票がまとまって回ってくる。こうした構造は、独立な到着を仮定した指数分布では表現できません。到着の間隔だけを見ていると平均は合いますが、瞬間的な混雑が再現されないので、待ち時間の分布の裾が実態より軽く出ます。連続する処理時間どうしの相関もあります。同じ担当者が続けて処理すれば慣れで短くなることもあり、疲労で長くなることもあります。設備の調子が悪い日は、その日の処理時間が全体的に長くなります。こうした系列相関があると、長い処理時間が連続して発生する確率が独立の仮定より高くなり、待ち時間の裾が伸びます。

確認の手順としては、まず入力どうしを散布図で見ることです。処理時間と案件の属性、到着時刻と処理時間、前の案件の処理時間と次の案件の処理時間。データの並び順を保ったまま隣り合う値の相関を計算すれば、系列相関の有無は簡単に確認できます。相関が見えたら、その構造をモデルに入れるか、入れないことによる影響を見積もります。相関を無視した場合の答えと、相関を入れた場合の答えを比べて、差が小さければ独立の仮定で進めてよいと判断できます。判断の根拠を記録に残すことが条件です。

本章の内容をまとめると、次のようになります。シミュレーションの答えは入力の分布で決まる部分が大きく、平均だけを入れたモデルは平均的な答えを返しません。同じ平均1.0分でも、ばらつきの形が違えば待ち時間は2.015分から6.386分まで開きます。したがって、現場に聞くべきは平均ではなく、ばらつきの大きさと分布の形です。データがあるなら層に分けて形を見て、無いなら仮定を明示して幅で持ち、どちらの場合も乱数の種とともに記録に残す。この工程を丁寧にやることが、モデルを信じてよいかどうかの土台になります。次章では、そのモデルを何回まわせばよいのかという、出力側の問いを扱います。

この章を深めたい方への参考書籍

Pythonコンピュータシミュレーション入門(橋本洋志・牧野浩二、オーム社)
人文・自然・社会科学の数理モデルをPythonで動かす入門書です。分布や乱数の扱いが結果をどう変えるかを、手を動かして確かめられます。

第9章 出力の分析、何回まわせば信じてよいのか

第8章まででモデルを組み立て、入力の分布を決めるところまで来ました。動かせばもう数字は出ます。平均待ち時間3.2分、平均在庫460個、1日あたり費用52.31。ここまで来ると、報告書に書ける形の数値が手元にあるので、仕事が終わったように見えます。この章で扱うのは、そこから先です。出てきた数値をどう読み、どこまで信じ、どう報告するか。工程としては地味ですが、シミュレーションの導入が失敗するときに原因になっている割合が高いのは、モデルの作りではなく、この出力の読み方のほうです。

理由ははっきりしています。シミュレーションの出力は、実験の測定値と同じで、必ずばらつくからです。乱数を使って動かしている以上、同じモデルを同じ条件で動かしても、乱数の種が違えば違う数字が出ます。ところが画面に表示されるのは小数点以下3桁まで揃った1つの数値です。3.2分という表示は、3.2分ぴったりであるかのような顔をしています。この見た目と実態の落差が、誤読の入り口になります。

本章の主張は2つに絞られます。1つは、シミュレーションの結果は1本の数字ではなく統計的な推定値であり、点推定と区間をセットで扱わなければ意味を持たないということ。もう1つは、必要な精度を先に決めてから計算量を決めるべきであって、「とりあえず10回まわした」という順番では案の優劣を判定できないということです。この2つを、実行した2本のコードの数値で確かめていきます。

1回の結果は標本の1つ、反復で標本を増やす

ある窓口の改善案を検討していて、シミュレーションを1回動かしたら平均待ち時間が3.2分と出たとします。この3.2分という数値は何を意味しているでしょうか。素直に読むと「この案を採用すれば平均3.2分になる」と読めます。しかし正しくは「乱数の種をこの値にしたときに、この長さの期間を1回動かした結果、平均が3.2分だった」です。種を変えれば3.6分になるかもしれませんし、2.9分になるかもしれません。

この性質は、シミュレーションの欠陥ではありません。むしろ現実の写しです。実際の窓口でも、同じ人員配置・同じ来客傾向のもとで営業しても、月曜日の平均待ち時間と火曜日の平均待ち時間は違います。誰が何分に来店するかは日によって違うからです。シミュレーションはその「日ごとの違い」を乱数で再現しているので、動かすたびに違う数字が出るのは当然の挙動です。1回の実行結果は、現実の1日を1日だけ観察した記録に相当します。

ここから素直に導かれる結論が、1回の結果で案の優劣を語ってはいけない、というものです。案Aを1回動かして3.2分、案Bを1回動かして3.0分だったとして、案Bのほうが良いとは言えません。両方とも、動かすたびに0.5分くらい平気で動く数値かもしれないからです。実際の現場で「先月は案Aの月で平均3.2分、今月は案Bの月で平均3.0分だったので案Bが優れている」と言われたら、「たった1か月ずつの比較では分からない」という反応が返ってくるのが普通だと思います。シミュレーションでも事情は同じです。

にもかかわらず、この誤りが実務で頻繁に起きるのには理由があります。1つは、シミュレーションが「計算」に見えることです。計算の結果は普通ばらつかないので、出てきた数字も確定値だと感じてしまいます。もう1つは、ツールの既定の表示が点推定だけになっていることが多いことです。区間を出すには追加の操作が要る設計になっていると、区間を出さないまま報告が流れます。

対処の方向は単純です。出力を1つの数値としてではなく、標本の1つとして扱う。標本が1つしかないなら増やす。増やして得られた標本の集まりから、平均と、そのばらつきの幅を出す。統計の初等的な手続きをそのまま当てはめるだけで、この問題はかなりの部分が片付きます。

同じ条件のまま、乱数の種だけを変えてモデルを何度も動かすことを反復と呼びます。レプリケーションとも言います。1回の反復から得られる要約統計量、たとえばその回の平均待ち時間を、1つの観測値とみなします。30回反復すれば、平均待ち時間という量について30個の観測値が手に入ります。この30個を通常の標本として扱い、平均と標準偏差を計算します。

この扱いが正当である理由は、反復どうしが独立だからです。1回目の実行で使った乱数と2回目の実行で使った乱数は無関係で、1回目の結果が2回目の結果に影響しません。統計の基本的な道具である平均と信頼区間は、標本が独立であることを前提にしています。反復はその前提をちょうど満たす形で作られています。逆に言えば、1回の実行の中で得られる個々の客の待ち時間は独立ではありません。混んでいるときに来た客は前後の客も一緒に待っているので、隣り合う観測値が強く相関します。1回の実行の中で客ごとの待ち時間から信頼区間を作ると、幅を過小に見積もります。この点は後半のバッチ平均法のところで改めて扱います。

反復の設計で守るべき決まりは3つあります。1つ目は、反復ごとに種を確実に変えることです。乱数生成器を毎回同じ種で初期化してしまうと、何回まわしても全部同じ結果が出ます。逆に、種の管理をせずに時刻ベースで初期化すると再現できなくなります。0, 1, 2, ... のように連番の種を使い、その範囲を記録に残す形が扱いやすい方法です。

2つ目は、条件を完全に揃えることです。反復のあいだで変わってよいのは乱数だけで、パラメータ・シナリオ・実行期間・データはすべて同じでなければなりません。3つ目は、各反復から取り出す要約統計量を先に決めておくことです。実行してから「やっぱり95%点も見たい」となると全部やり直しになります。平均・最大・分位点・目標達成率など、見る可能性のある指標は最初から全部保存しておくほうが結局は安く済みます。

反復から得られた \( n \) 個の観測値を \( X_1, \dots, X_n \) とすると、点推定は標本平均 \( \bar{X} \)、区間は \( \bar{X} \pm t_{n-1,\,1-\alpha/2} \cdot s / \sqrt{n} \) です。ここで \( s \) は標本標準偏差、\( t \) は自由度 \( n-1 \) のt分布の値です。この \( t_{n-1,\,1-\alpha/2} \cdot s / \sqrt{n} \) の部分を信頼区間の半幅と呼びます。区間の幅は半幅の2倍です。実務では、この半幅こそが「何回まわすか」を決める材料になります。

反復回数と区間幅、精度を2倍にするには4倍まわす

反復回数を増やすと区間がどう縮むかを確かめました。1回の実験で得られる指標が平均12.0・標準偏差4.0で散らばる状況を想定し、反復回数を5回から1000回まで変えて、そのつど推定平均と95%信頼区間を計算しています。

import numpy as np

rng = np.random.default_rng(7)

# 1回の実験で得られる指標が平均12.0・標準偏差4.0で散らばるとする
TRUE_MEAN, TRUE_SD = 12.0, 4.0

print("%-8s %-10s %-22s %-10s" % ("反復回数", "推定平均", "95%信頼区間", "区間幅"))
for n in (5, 10, 30, 100, 300, 1000):
    x = rng.normal(TRUE_MEAN, TRUE_SD, n)
    half = 1.96 * x.std(ddof=1) / np.sqrt(n)
    print("%-8d %-10.3f [%7.3f, %7.3f]  %-10.3f"
          % (n, x.mean(), x.mean() - half, x.mean() + half, 2 * half))

# 区間幅を目標以下にするのに必要な反復回数
for target in (2.0, 1.0, 0.5):
    n_need = int(np.ceil((2 * 1.96 * TRUE_SD / target) ** 2))
    print("区間幅を%.1f以下にするのに必要な反復回数: 約%d回" % (target, n_need))

実行結果は次のとおりです。

反復回数     推定平均       95%信頼区間                区間幅
5        10.944     [  9.361,  12.528]  3.167
10       11.715     [  9.942,  13.489]  3.547
30       10.214     [  8.910,  11.519]  2.609
100      11.641     [ 10.933,  12.348]  1.415
300      11.710     [ 11.281,  12.140]  0.859
1000     11.848     [ 11.601,  12.095]  0.494
区間幅を2.0以下にするのに必要な反復回数: 約62回
区間幅を1.0以下にするのに必要な反復回数: 約246回
区間幅を0.5以下にするのに必要な反復回数: 約984回
反復回数推定平均区間幅前の行からの反復倍率前の行からの幅の比
510.9443.167比較の起点比較の起点
1011.7153.5472.0倍1.12倍(増えている)
3010.2142.6093.0倍0.74倍
10011.6411.4153.3倍0.54倍
30011.7100.8593.0倍0.61倍
100011.8480.4943.3倍0.58倍

まず読み取れるのは、反復を増やすほど区間が縮み、推定平均が真の値12.0に寄っていくことです。反復5回のときの推定平均は10.944で、真の値から1.056もずれています。1000回まわすと11.848になり、ずれは0.152まで小さくなります。区間幅は3.167から0.494へ、およそ6分の1になりました。

次に、縮み方の速さです。表の右の2列を見ると、反復を約3倍にするたびに、幅がおよそ0.58倍になっています。\( 1/\sqrt{3} \) はおよそ0.577ですから、幅は反復回数の平方根に反比例して縮んでいます。半幅の式に \( \sqrt{n} \) が分母として入っているのですから、当然の挙動です。ただし、この当然の性質が実務に与える影響は小さくありません。

必要反復回数の計算がそれを直接示しています。区間幅2.0以下にするには約62回、1.0以下にするには約246回、0.5以下にするには約984回。目標の幅を半分にするたびに、必要な反復回数は約4倍になっています。62から246が3.97倍、246から984が4.00倍です。精度を2倍にするには反復を4倍にする必要があるという平方根の法則が、この3行に出ています。

この法則は、計算資源の見積もりに直結します。1回の反復に10秒かかるモデルなら、62回で約10分、246回で約41分、984回で約2時間44分です。ここまでは待てます。ところが1回の反復に10分かかるモデルだと、62回で約10時間、246回で約41時間、984回で約1週間になります。「もう少し精度がほしい」という一言が、待ち時間を4倍にします。精度の要求は、決める前に計算量に換算して確認する習慣を持つべき性質のものです。

逆向きにも使えます。使える計算時間が決まっているなら、そこから達成できる精度が逆算できます。並列実行できる環境なら、反復どうしは独立なので素直に台数分だけ速くなります。この点は、反復が独立であることのもう1つの利点です。

反復回数と信頼区間の幅の関係を示す概念グラフ

少ない反復では区間幅すら信用できない、だから精度から先に決める

この結果には、平方根の法則だけでは説明できない箇所が1つあります。反復5回のときの区間幅が3.167、10回のときが3.547で、反復を倍にしたのに区間が広がっています。理屈のうえでは幅は \( 1/\sqrt{2} \) 、およそ0.707倍に縮むはずなのに、実測では1.12倍に広がりました。

これは計算の誤りではなく、少ない反復に固有の性質です。半幅の式は \( 1.96 \cdot s / \sqrt{n} \) で、分子に標本標準偏差 \( s \) が入っています。\( s \) は真の標準偏差4.0そのものではなく、手元の少数の観測値から推定した値です。5個や10個しかない観測値から標準偏差を推定すると、その推定自体が大きくばらつきます。たまたま似た値ばかり5個出れば \( s \) は小さく出て、区間は狭く見えます。たまたま散らばった値が10個出れば \( s \) は大きく出て、区間は広く見えます。今回の実行では、この揺れが \( \sqrt{n} \) による縮みを打ち消して逆転したわけです。

ここから引き出すべき結論は、少ない反復では、区間の幅そのものが信用できないということです。区間は「推定の不確かさ」を表すはずのものですが、その区間の推定自体が不確かなのですから、狭い区間が出ても安心する根拠になりません。「10回まわしたら区間が狭かったので十分だと判断した」という進め方は、この意味で危険です。狭かったのが実力なのか偶然なのかを、その10回からは区別できません。

加えて、このコードには1つ簡略化があります。半幅の計算に、t分布の値ではなく正規分布の1.96を使っている点です。反復が多ければ差はほとんどありませんが、反復5回のときの自由度4に対するt値は2.776で、1.96より4割ほど大きい値です。つまり本来の区間は、表示された3.167よりさらに広くなります。少数反復の区間は、広く見積もっても足りないくらいだ、と理解しておくのが安全です。実務のコードでは、反復が30回を下回る場面ではt分布の値を使ってください。

実務上の目安としては、区間を根拠として使うなら反復は最低でも数十回、案の比較のように微差を扱うならさらに多くという水準になります。ただし、この「最低でも数十回」も絶対の基準ではありません。回数を先に決めてしまうやり方では、その回数で足りているかどうかを、その回数の結果からは確かめられないからです。

正しい進め方が「回数を決めてから測る」ではなく「必要な精度から回数を決める」になるのは、このためです。実務での順番は次のようになります。まず、意思決定にとって意味のある差の大きさを決めます。次に、その差を見分けられる区間幅を目標として設定します。最後に、その幅を達成する反復回数を計算します。この順番であれば、出てきた結果が判定に使えることが最初から保証されます。

意味のある差の大きさは、業務側から決まります。窓口の待ち時間なら「30秒縮まるなら投資する価値がある」、在庫費用なら「日額で1万円変われば設備を入れ替える」といった水準です。この基準を持たずに計算を始めると、区間が0.5分だったときにそれが十分に狭いのか広いのかを判断できません。基準は技術的に決まるものではないので、モデルを作る側が勝手に決めず、意思決定する側に確認する必要があります。

目標の幅が決まれば、必要反復回数は計算できます。手順は2段階です。まず少数の反復、たとえば10回から20回を予備的に走らせて、標準偏差 \( s_0 \) を粗く見積もります。次に \( n \ge \left( 2 z s_0 / d \right)^2 \) を計算します。\( d \) が目標の幅、\( z \) は95%区間なら1.96です。今回のコードが計算しているのはこの式です。予備の反復で得た \( s_0 \) はあくまで粗い推定なので、本番の反復を回したあとに実際の幅を確認し、足りなければ追加で回します。この「回して、測って、足す」という進め方を逐次法と呼びます。

この設計をしておくと、報告の形も変わります。「10回まわしたら3.2分でした」ではなく、「区間幅を0.5分以内にするという目標のもとで246回まわし、3.21分、95%区間は3.00分から3.42分でした」と書けます。後者は、読んだ人が「案Bの3.0分との差は判定できていない」と自分で判断できます。前者からは何も判断できません。

なお、精度を上げる方法は反復を増やすことだけではありません。同じ反復回数のまま区間を狭める技法があり、これを分散減少法と呼びます。乱数の使い方を工夫して、比較したい案のあいだで条件を揃える方向の技法です。第11章で扱います。計算時間が制約になる場面では、反復を4倍にする前に、まずこちらを検討する価値があります。

終わりのある系と終わりのない系、区別しないと分析を誤る

ここまでは「1回の反復」という単位を当然のものとして使ってきました。しかし、1回の反復が何を指すかは、対象の系の性質によって変わります。この区別を先に付けておかないと、あとの分析全体が噛み合わなくなります。

1つ目の型は、終わりのある系です。英語では terminating と呼ばれます。始まりと終わりがはっきりしていて、終了条件が業務上の意味を持つ系のことです。朝9時に開店して夕方5時に閉店する窓口、1日分の受注をまとめて処理するバッチ処理、開会から閉会までのイベント運営などが当たります。この型では、1日を1反復として扱えば済みます。開店時の状態は毎日ほぼ同じ、つまり客がいない状態から始まるのが現実そのものなので、空の状態から始めることに何の問題もありません。

2つ目の型は、終わりのない系です。non-terminating と呼ばれます。24時間動き続ける工場、常時稼働する通信網、止まらない物流拠点のように、区切りがなく、長い目で見た定常状態の性能に関心がある系です。この型では、1回の反復をどこからどこまでにするかを自分で決めなければなりません。そして、モデルは必ず空の状態から始まるのに、知りたいのは混雑が落ち着いたあとの状態なので、両者のずれを処理する必要が出てきます。

観点終わりのある系終わりのない系
9時開店17時閉店の窓口、1日分のバッチ処理24時間稼働の工場、通信網、物流拠点
知りたいこと1日全体としての性能落ち着いたあとの定常状態の性能
1反復の単位1日(業務が決めてくれる)自分で決める(十分に長く取る)
初期状態空でよい。現実がそうだから空から始まるが、知りたい状態ではない
立ち上がりの扱い捨てない。立ち上がりも業務の一部捨てるか、影響が無視できるまで長く回す
標本の作り方1日を1反復として複数日ぶん回す反復を重ねる、またはバッチ平均法

この区別で最も誤りやすいのは、終わりのある系で立ち上がりを捨ててしまうことです。開店直後に待ち時間が短いのは、モデルの都合ではなく現実です。その時間帯を切り捨てると、1日の平均待ち時間を実際より長く見積もることになります。逆に、終わりのない系で立ち上がりを捨てないと、後で見るとおり平均を実際より小さく見積もります。どちらの誤りも、方向が決まっている系統的なずれなので、反復を増やしても消えません。

判断の基準は「その系にとって、空の状態から始まることが現実か」です。現実なら捨てない。現実でないなら捨てる。この一行で大半の場面は決まります。迷いやすいのは、24時間稼働だが週末に停止する工場のような中間的な系です。この場合は、関心が週内の定常性能にあるのか、週の立ち上げも含めた実績にあるのかを、意思決定の側に確認して決めます。

立ち上がり期間を捨てる、ウェルチの手法

終わりのない系で、空の状態から始めた直後の期間を立ち上がり期間と呼びます。ウォームアップ期間、過渡期とも言います。この期間は、系の中がまだ埋まっていないので、本来の定常状態より空いています。空いていれば待ちは短く、系内の人数も少なくなります。この区間を含めて平均を取ると、平均は下に引っ張られます。

どれだけ引っ張られるかを実測しました。到着率0.9・サービス率1.0の M/M/1 の系を、時間4000まで30回反復し、1時点ごとに系内人数を記録します。30回ぶんを時点ごとに平均してから移動平均で均し、落ち着く時点を探しています。この系の定常時の平均系内人数は \( \rho / (1-\rho) \) で計算でき、\( \rho = 0.9 \) なので9.0人です。真の値が分かっている題材を使っているので、捨てる長さと誤差の関係を直接測れます。

import numpy as np
import simpy

LAM, MU, HORIZON, N_REP = 0.9, 1.0, 4000, 30

def queue_length_series(seed):
    rng = np.random.default_rng(seed)
    env = simpy.Environment()
    server = simpy.Resource(env, capacity=1)
    series = []

    def customer(env):
        with server.request() as req:
            yield req
            yield env.timeout(rng.exponential(1 / MU))

    def source(env):
        while True:
            yield env.timeout(rng.exponential(1 / LAM))
            env.process(customer(env))

    def monitor(env):
        while True:
            series.append(len(server.queue) + len(server.users))
            yield env.timeout(1.0)

    env.process(source(env))
    env.process(monitor(env))
    env.run(until=HORIZON)
    return np.array(series[:HORIZON])

runs = np.array([queue_length_series(s) for s in range(N_REP)])
avg = runs.mean(axis=0)                      # Welchの手法:反復をまたいで平均
w = 25
smooth = np.convolve(avg, np.ones(2 * w + 1) / (2 * w + 1), mode="valid")

rho = LAM / MU
theory = rho / (1 - rho)                     # M/M/1 の定常時の平均系内人数

print("理論値(定常)      :", round(theory, 3), "人")
for cut in (0, 50, 100, 200, 300, 400):
    print("先頭%5d時点を捨てた平均: %6.3f  (誤差 %+.3f)"
          % (cut, avg[cut:].mean(), avg[cut:].mean() - theory))
print("\n移動平均が定常値の5%以内に入る最初の時点:",
      int(np.argmax(np.abs(smooth - theory) < 0.05 * theory) + w))

実行結果です。

理論値(定常)      : 9.0 人
先頭    0時点を捨てた平均:  8.851  (誤差 -0.149)
先頭   50時点を捨てた平均:  8.916  (誤差 -0.084)
先頭  100時点を捨てた平均:  8.942  (誤差 -0.058)
先頭  200時点を捨てた平均:  8.979  (誤差 -0.021)
先頭  300時点を捨てた平均:  8.984  (誤差 -0.016)
先頭  400時点を捨てた平均:  9.014  (誤差 +0.014)

移動平均が定常値の5%以内に入る最初の時点: 228

先頭を1つも捨てないと、平均は8.851で、真の値9.0より0.149人だけ小さく出ます。捨てる長さを増やすにつれて誤差は縮み、50時点で0.084、100時点で0.058、200時点で0.021、300時点で0.016、400時点で0.014になります。捨てる長さと誤差が単調に対応しており、初期の空いている時間帯が平均を下に引いていたことがはっきり分かります。

この例では誤差の絶対値が0.149と小さく見えるかもしれません。理由は、実行期間を4000時点と長く取っているためです。立ち上がりが仮に200時点ぶんあるとしても、全体の5%にすぎないので、薄まって見えます。実行期間が短いほど、立ち上がりの占める割合が大きくなり、ずれも大きくなります。実行期間400時点で立ち上がり200時点なら、半分が汚染されていることになります。立ち上がりの影響の大きさは、絶対的な長さではなく、実行期間に対する比率で決まります。

もう1つ重要な性質があります。このずれは、反復を増やしても消えません。今回はすでに30回の反復を平均した値を見ています。それでも8.851に留まっているのは、すべての反復が同じように空から始まり、同じ方向にずれているからです。反復を100回に増やせば、8.851という値の推定精度は上がりますが、8.851が9.0に近づくことはありません。これは偶然のばらつきではなく偏りなので、標本を増やす方法では取り除けません。取り除けるのは、捨てるか、実行期間を延ばして薄めるかのどちらかです。

捨てる長さの決め方が次の問題になります。ここで使うのがウェルチの手法です。手順は3段階です。第1に、複数の反復を同じ条件で走らせ、時点ごとの値を反復にまたがって平均します。コードでは runs.mean(axis=0) がこれに当たります。1回の系列は上下に激しく振れていて、どこで落ち着いたかを目で判断できませんが、30回ぶんを平均すると振れが小さくなり、立ち上がりの形が見えるようになります。

第2に、平均した系列にさらに移動平均をかけて滑らかにします。コードでは窓幅25の前後、つまり51時点の移動平均を np.convolve で計算しています。第3に、滑らかにした系列を目で見て、水平になった時点を立ち上がりの終わりと判断します。今回は真の値が分かっているので、機械的に「定常値の5%以内に入る最初の時点」を求めていて、その答えが228でした。実務では真の値が分からないので、この最後の段階は図を見ての判断になります。

228という数値が意味するのは、この条件では200時点程度を捨てれば十分だということです。実測の表とも整合します。200時点捨てたときの誤差は0.021で、300時点や400時点まで捨てても0.016や0.014にしかならず、改善は頭打ちです。捨てすぎると使えるデータが減って区間が広がるので、必要以上に捨てる利点はありません。

実務で押さえておくべき注意を3つ挙げます。1つ目は、立ち上がりの長さは条件ごとに変わることです。利用率が高いほど定常に達するのが遅くなります。この例の \( \rho = 0.9 \) は混んでいる部類なので長めですが、\( \rho = 0.5 \) ならもっと早く落ち着きます。シナリオを変えたら、立ち上がりの長さも測り直してください。2つ目は、指標ごとに落ち着く速さが違うことです。稼働率は早く落ち着き、待ち時間の分布の裾は遅く落ち着きます。最も遅い指標に合わせて捨てる長さを決めるのが安全です。3つ目は、捨てる代わりに現実的な初期状態から始めるという方法もあることです。前日の終業時の在庫や仕掛品をそのまま初期値に置ければ、立ち上がりはかなり短くなります。

ウェルチの手法の3段階を示すフロー図

バッチ平均法、長く1回まわして区間に切る

終わりのない系を扱うもう1つの方法がバッチ平均法です。反復を何度も行う代わりに、非常に長く1回だけ走らせ、その系列を等しい長さの区間に切って、各区間の平均を1つの標本として扱います。区間のことをバッチと呼びます。

利点は、立ち上がりを1回だけ捨てれば済むことです。反復を30回行う方式では、30回すべてが空の状態から始まるので、30回ぶんの立ち上がりを捨てることになります。今回の条件で200時点ずつ捨てるなら、合計6000時点ぶんの計算が捨てられます。バッチ平均法なら、最初の200時点を1回捨てるだけで、残りはすべて使えます。1回の立ち上がりが長い系、たとえば定常に達するまでに数週間ぶんの模擬時間が必要な系では、この差が効きます。

代償は、バッチどうしが独立でないことです。反復は乱数から独立ですが、同じ1回の実行を切り分けたバッチは時間的に連続しているので、前のバッチの終わりの状態が次のバッチの始まりの状態になります。混んだまま次のバッチに入れば、次のバッチの平均も高く出ます。この相関を無視して通常の信頼区間を計算すると、幅を実際より狭く見積もります。狭い区間は「精度が高い」という誤った印象を与えるので、危険な方向の誤りです。

対処は、バッチを十分に長く取ることです。バッチが長ければ、境界での引き継ぎの影響が全体に占める割合が小さくなり、バッチ間の相関は薄くなります。実務では、バッチの数を20から30程度に固定し、バッチの長さを伸ばしていって、隣り合うバッチの相関係数が十分小さくなるところを探す手順が使われます。相関の検定を組み込んで自動でバッチ長を決める方法もあります。

観点反復(独立反復法)バッチ平均法
やり方種を変えて短めの実行を多数回1回を非常に長く回して区間に切る
標本の独立性独立。通常の信頼区間がそのまま使える独立でない。バッチ長の確認が要る
立ち上がりの負担反復の回数だけ捨てる最初の1回だけ捨てる
並列実行反復ごとに並列化できる1本の実行なので並列化しにくい
向く場面立ち上がりが短い、計算資源を並べられる立ち上がりが長い、1回の実行が高価
終わりのある系こちらを使う(1日=1反復)使わない

選び方の目安は単純です。終わりのある系なら反復一択です。終わりのない系で、計算資源を並列に使えて立ち上がりが短いなら反復。立ち上がりが長く、1回の実行に手間がかかるならバッチ平均法。判断がつかないときは反復から始めるほうが安全です。区間の解釈を誤る危険が小さいからです。

案の比較は、それぞれの区間ではなく差の区間で見る

ここまでは1つの案の性能を推定する話でした。実務でより多いのは、複数の案を比べる場面です。ここで頻出する誤りが、2つの案それぞれの信頼区間を並べて、重なっているかどうかで判定してしまうことです。

この方法は正しくありません。案Aの区間が3.0から3.4、案Bの区間が3.2から3.6で重なっていたとしても、両者に差がないとは言えません。逆に、区間が重なっていなければ差があると言えますが、その判定は必要以上に厳しく、本当は検出できる差を見逃します。区間の重なりで判定する方法は、片側に寄った誤りを生みます。

正しい扱いは、差そのものを推定することです。反復 \( i \) について、案Aの結果 \( A_i \) と案Bの結果 \( B_i \) の差 \( D_i = A_i - B_i \) を作ります。この \( D_i \) を標本として、平均 \( \bar{D} \) と信頼区間を計算します。判定の規則も単純で、区間が0をまたぐなら「差があるとは言えない」、またがないなら「差がある」です。

「差があるとは言えない」は「差がない」ではありません。この2つを混同すると、報告が誤った断定になります。区間が0をまたぐという結果が意味するのは、手元の反復回数では判定に足りない、ということだけです。対応は2つあります。反復を増やして区間を狭めるか、そもそもその差が意思決定に影響しない大きさなら判定を諦めて別の観点で決めるかです。どちらを選ぶかは、追加の計算コストと、決定の重要度の兼ね合いで決まります。

差の区間を使うと、もう1つ都合の良いことが起きます。両方の案を同じ乱数で動かせば、\( A_i \) と \( B_i \) が同じ方向に動くため、差 \( D_i \) のばらつきが小さくなり、区間が狭くなります。同じ反復回数のまま判定力が上がるということです。これが共通乱数法で、その効果を実測した数値とあわせて第11章で扱います。

3案以上を比べる場合は、注意が1つ増えます。すべての組み合わせについて差の区間を作ると、比較の回数が増えるぶん、偶然に「差がある」と出てしまう確率が上がります。10案なら45組の比較になるので、95%の区間を45回作れば、そのうち2組程度は偶然で有意に見えます。この扱いは、多重比較の補正や、最良の案を選ぶことに特化したランキングと選択の手法で対処します。ランキングと選択は第12章で扱います。

平均以外の指標を見る、そして報告の作法を決める

報告のもう1つの問題は、指標の選び方です。シミュレーションの出力として最初に見るのは平均ですが、平均だけを報告すると、意思決定に使いにくい報告になります。

理由は、業務の目標が平均で書かれていないことが多いからです。コールセンターの目標は「20秒以内に応答する呼の割合を80%以上」という形で書かれます。配送の約束は「95%の注文を翌日までに届ける」です。製造の納期遵守率も、平均リードタイムではなく、期限に間に合った割合で測られます。経営が外部に約束するのは平均ではなく上限や達成率であることが多いので、平均だけの報告は、目標に対する答えになっていません。

もう1つの理由は、待ち時間や滞在時間の分布が左右対称ではないことです。第5章で見たとおり、混雑した系の待ち時間は右に長い裾を持ちます。平均3.2分の系で、95%点が12分ということは普通に起こります。平均だけを見ていると、その12分を経験する客が20人に1人いることが見えません。苦情や離脱を生むのは平均の客ではなく裾の客です。

指標何が分かるか使いどころ
平均全体の水準。他の指標の基準になる案どうしの大まかな比較、リトルの法則との突き合わせ
中央値典型的な1件。裾に引きずられない平均と大きく離れていれば分布の歪みを疑う
95%点・99%点悪いほうの水準。裾の厚さサービス水準の約束、苦情や離脱の見積もり
最大値最悪の1件安全性や設備容量の設計。ただし反復ごとに大きく振れる
目標達成率目標を満たした割合契約上のサービス水準、社内KPIとの直接の対応
分布の形山が1つか2つか、裾がどこまで伸びるか平均や分位点だけでは見えない構造の発見

これらの指標にも、平均と同じく信頼区間が必要です。各反復から95%点を1つずつ取り出せば、95%点についての標本ができ、その平均と区間を作れます。分位点の推定は平均の推定より多くの標本を要するので、95%点を精度よく出したいなら、平均を出すときより多くの反復が必要になる点は意識しておいてください。最大値はとくに反復ごとの振れが大きく、区間が広くなります。最大値を単独の根拠にするのは避け、99%点などと併せて見るのが実務的です。

分布の形は、数値ではなく図で確認します。ヒストグラムを描いて山が2つあれば、性質の異なる2種類が混ざっている可能性があります。たとえば、すぐに処理される客と、特殊な手続きが必要で長くかかる客が混ざっている場合です。この構造が見えると、改善案の方向も変わります。全体の処理を速くするのではなく、長くかかる種類だけを別の窓口に分ける、という案が候補に上がるからです。平均だけを見ていると、この発想は出てきません。

指標を選んだら、最後に報告の形を決めておきます。シミュレーションの結果を文書やスライドに載せるとき、次の4点を必ず書きます。逆に、この4点が書かれていない報告は、受け取った側が追試も検証もできません。

  1. 点推定と区間をセットで書く。「平均待ち時間3.21分」ではなく「平均待ち時間3.21分、95%信頼区間は3.00分から3.42分」と書きます。区間なしの数値は、精度が不明という意味で、読み手が判断に使えません。
  2. 反復回数と乱数の種を書く。「反復246回、種は0から245の連番」のように書きます。反復回数は精度の根拠であり、種は再現の根拠です。この2つがあれば、別の人が同じ結果を再現できます。
  3. 立ち上がりの扱いを書く。「終わりのない系として扱い、先頭200時点を除外」あるいは「終わりのある系として1日を1反復とし、除外なし」と書きます。この扱いの違いだけで結論の向きが変わりうるので、省略できません。
  4. 比較は差で書く。「案Aと案Bの差は0.21分、95%区間は0.05分から0.37分。区間が0をまたがないため差があると判断した」と書きます。それぞれの区間を並べるだけの表にしません。

この4点に加えて、余裕があれば書いておきたいことが2つあります。1つは、平均以外の指標を少なくとも1つ添えることです。95%点か目標達成率のどちらかがあれば、平均だけの報告より格段に使いやすくなります。もう1つは、シナリオの前提を1行で書くことです。到着率、サービス時間の分布、窓口数、実行期間。第8章で決めた入力の前提が、そのまま結果の前提になります。

報告の書式を組織で統一しておくと、副次的な効果があります。書式に「区間」「反復回数」「立ち上がり」の欄があれば、埋めるために必要な作業が自動的に発生します。区間の欄が空欄のまま提出されることを許さなければ、区間を出さない分析は成立しなくなります。分析の質を人の注意力に頼らず、書式で担保する方法です。

ここまでで、出力を統計的な推定値として扱う手続きが揃いました。ただし、精度の高い推定値が得られたからといって、その値が現実を写しているとは限りません。区間の狭さが保証しているのは「同じモデルを何度動かしても同じ数字が出る」ことだけであって、「そのモデルが現実と合っている」ことではありません。区間が狭い間違った答えというものが存在します。モデルが現実と合っているかをどう確かめるかは、次の第10章で扱います。

この章を深めたい方への参考書籍

Simulation Modeling and Analysis 第6版(Averill M. Law、McGraw-Hill)
シミュレーションの標準的な参考書です。妥当なモデルの作り方、出力データの分析、代替案の比較、分散減少法、実験計画と最適化がそれぞれ独立した章として扱われており、本コラムの後半の各章に対応します。

第10章 検証と妥当性確認、モデルを信じるための手続き

ここまでの章で、モデルの型を選び、入力の分布を決め、必要な反復回数を見積もる手順を扱ってきました。技術的にはこれで数字が出ます。ところが、数字が出たあとに待っているのは別種の問題です。作った本人以外の誰も、その数字を信じてくれないという事態が起こります。

シミュレーションが現場で使われない最大の理由は、精度ではありません。信頼です。小数第2位まで合っているモデルでも、現場の責任者が「うちの現場はそんな動き方をしない」と一言いえば、その日から誰も開かなくなります。逆に、粗いモデルでも、作る過程に現場が関わっていて、どこまで信じてよいかが共有されていれば、意思決定の材料として機能します。数字の質と、数字が使われるかどうかは、別の軸で決まります。

本章では、その信頼を手続きとして組み立てる方法を扱います。感覚や人柄で信頼を得るのではなく、決まった手順を踏んで、踏んだ記録を残すことで信頼を作ります。手続きにする理由は再現性です。作った人が異動しても、手順と記録が残っていれば、次の担当者が同じ水準の信頼を引き継げます。

最初に、混同されやすい2つの作業を分けるところから始めます。この区別が曖昧なままだと、実装のバグを現実の複雑さのせいにしたり、逆に前提の取り違えをプログラムの問題として延々と探したりすることになります。

検証と妥当性確認は、別の作業である

検証とは、作ろうとしたモデルを正しく作れているかを確かめる作業です。英語では verification と呼ばれます。設計した論理どおりにプログラムが動いているか、式の書き間違いがないか、単位が揃っているか、乱数の使い方が意図どおりか。対象はあくまでプログラムの内部であり、現実の世界は登場しません。設計図とコードを突き合わせる作業だと考えると分かりやすいと思います。

妥当性確認とは、作るべきモデルを作っているかを確かめる作業です。英語では validation と呼ばれます。そのモデルの構造と挙動が、答えたい問いに対して現実を十分に写しているか。ここでは現実の世界が主役になります。現場の運用ルールを取り違えていないか、無視した要素が結論をひっくり返すほど効いていないか、過去に実際に起きたことをモデルでも起こせるか。設計図そのものが正しいかを問う作業です。

この2つを並べると、検証は内向きの作業、妥当性確認は外向きの作業だと整理できます。検証は自分たちだけで完結します。コードを読み、テストを書き、理論値と比べれば済みます。妥当性確認は自分たちだけでは完結しません。現場の人、実績データ、業務の文書といった外部の情報がなければ判定できません。必要な体制も、かかる時間も違います。

両者を混同すると、典型的な失敗が2種類起きます。1つは、実装のバグを現実の複雑さのせいにすることです。待ち時間が想定より長く出たときに「現場はもっと複雑だから」と説明してしまい、実はサービス時間の単位を分と秒で取り違えていた、という筋のずれ方です。もう1つは逆で、前提の取り違えをプログラムの問題として探し続けることです。コードは1行も間違っていないのに、現場が実際には優先順位をつけて処理していることを知らないまま、待ち行列を先着順で組んでいた、という場合がこれにあたります。

順序としては、検証を先に済ませます。実装が意図どおりに動いていない状態で現実と突き合わせても、ずれの原因が実装なのか前提なのか切り分けられないためです。検証をひととおり通してから、妥当性確認に進みます。ただし実務では完全な直列にはならず、妥当性確認の過程で見つかった前提の誤りを直し、直したコードをまた検証する、という往復になります。

観点検証(verification)妥当性確認(validation)
問い作ろうとしたモデルを、正しく作れているか作るべきモデルを、作っているか
比べる相手設計・仕様・理論値現実・実績データ・現場の知識
主な担当モデルを作った技術者技術者と現場の担当者の共同
見つかる誤り実装の誤り、単位の誤り、論理の抜け前提の誤り、要素の取りこぼし、範囲の取り違え
完了の判定設計どおりに動くことが確認できた目的に対して十分だと関係者が合意した
やらないとどうなるか数字そのものが誤る正しく計算した誤った数字が出る

表の最終行が、両者の危険性の違いを表しています。検証を怠ると数字が壊れます。壊れた数字は、極端な値になれば気づけることも多くあります。妥当性確認を怠った場合は、計算としては正しい数字が出てきます。もっともらしい値なので誰も疑わず、そのまま意思決定に使われます。後者のほうが発見が遅れやすく、実害が大きくなりやすいという性質があります。

検証と妥当性確認の位置づけを表す図

検証の手法、実装が意図どおりかを確かめる

検証の手法は、いずれも「答えが分かっている状況を作り、その答えが出るか見る」という発想でできています。現実はどう動くか分からないので比べる相手になりませんが、単純な条件に落とせば、手計算や理屈で答えが決まる状況が作れます。そこで一致しないモデルは、複雑な条件でも正しく動きません。

最も基本的なのが理論値との突き合わせです。第4章では、M/M/1 の条件、つまり到着がポアソン過程、サービス時間が指数分布、窓口が1つという設定に落として、待ち時間の理論値と実測を比べました。利用率0.8のときの理論値は4.0分です。これに対してシミュレーションの推定値は4.0307分、10回の独立した試行から求めた95%信頼区間は [3.9541, 4.1073] で、区間の中に理論値が入りました。この手続きが理論値との突き合わせにあたります。

この方法の要点は、比べるときに信頼区間を使うことです。シミュレーションの出力は乱数によって毎回ぶれるので、点の値どうしを比べても意味がありません。4.0307という数字が4.0と違うことは、それ自体では何も意味しません。区間の中に理論値が入るかどうかで判定します。もし区間が理論値を外していれば、乱数のぶれでは説明できないずれがあるということになり、実装を疑う根拠になります。

次に、極端な入力での挙動確認です。到着をゼロにすれば待ち時間はゼロになるはずです。資源の数を無限に近づければ、やはり待ちはゼロに近づくはずです。逆に利用率を1に近づければ、待ち行列は発散して増え続けるはずです。サービス時間を2倍にすれば、少なくとも待ちが減ることはないはずです。こうした「こうなるに決まっている」条件を並べて実行し、想定どおりに動くかを見ます。極端な条件では、通常の条件では埋もれてしまう符号の誤りや条件分岐の抜けが表に出やすくなります。

保存則の確認も有効です。系に入った数と、出た数と、まだ中に残っている数の関係は必ず成り立ちます。入った数が出た数と滞留数の合計に一致しない場合、エンティティをどこかで消しているか、二重に数えています。時間についても同じ確認ができます。1つの資源について、稼働していた時間と、空いていた時間と、故障で止まっていた時間を足すと、シミュレーション全体の時間に一致するはずです。この帳尻が合わない場合、状態遷移のどこかに抜けがあります。

トレースは、1体のエンティティの一生を時刻つきで印字し、人の目で追う方法です。到着した時刻、列に並んだ時刻、資源を確保した時刻、処理を終えた時刻、退出した時刻を並べて、時刻が単調に増えているか、処理時間が設定した分布の範囲に収まっているか、資源の確保と解放が対になっているかを確かめます。地味な作業ですが、集計値を見ているだけでは絶対に見つからない種類の誤りが出てきます。全体の統計は正常に見えるのに、特定の条件のエンティティだけが列を追い越していた、というような誤りです。

手法何をするか見つかりやすい誤り手間
理論値との突き合わせ単純な条件に落とし、解析解と信頼区間で比べる式の誤り、乱数の使い方、集計方法の誤り
極端な入力到着ゼロ、資源無限、利用率を1に近づけるなどを試す符号の誤り、条件分岐の抜け、境界の扱い
保存則の確認入った数と出た数と滞留数、時間の内訳の帳尻を合わせるエンティティの消失、二重計上、状態遷移の抜け
トレース1体の一生を時刻つきで印字し、手で追う順序の誤り、資源の解放漏れ、優先順位の実装ミス
アニメーション動かして目で見る。現場の人に見せる現場の常識と違う動き、あり得ない滞留
退化した条件資源1つ、確率なしの決定的な設定で答えを照合基本ロジックの誤り、初期化の誤り
コードレビュー別の人が設計意図と実装を突き合わせて読む思い込みによる誤り、仕様の解釈違い
単体テスト部品ごとに入力と期待出力を固定して自動で確認変更にともなう退行、部品の境界条件

モデルを壊しに行く、退化した条件とアニメーション

退化した条件での照合は、極端な入力と似ていますが、狙いが少し違います。極端な入力が「振る舞いの向き」を見るのに対して、退化した条件では「決定的な答え」との一致を見ます。確率を一切使わず、到着間隔を5分固定、サービス時間を3分固定、窓口を1つにすれば、誰が何時に到着して何時に出ていくかを紙に書き出せます。その紙の答えと、シミュレーションの出力が完全に一致するかを確かめます。乱数が絡まないので、1件でも合わなければ実装の誤りだと確定できます。

この確定できるという性質が重要です。乱数が入った比較では、ずれが実装の誤りによるものか、単なる偶然のぶれかを切り分けるのに統計的な判断が要ります。乱数を外した比較では判断が不要になります。合うか合わないかの二択になるので、誤りの有無を機械的に判定できます。新しい機能を追加するたびに、この決定的な条件を通す習慣をつけておくと、変更によって壊れた箇所がその場で分かります。

アニメーションは、モデルの動きを画面上で可視化して目で見る方法です。エンティティが図面の上を移動し、資源の色が空きと稼働で変わり、列が伸び縮みする様子を表示します。手間はかかりますが、検証の手法の中で、現場の人が最も違和感に気づきやすいという特長があります。数表を見せても何も言わなかった現場担当者が、動きを見た瞬間に「この工程からあっちに直接行くことはない」と指摘する、という形で誤りが出てきます。

アニメーションの位置づけには注意が必要です。動きが自然に見えることは、モデルが正しいことの証明にはなりません。見た目を整えることに時間を使いすぎると、内部の論理を確かめる作業がおろそかになります。あくまで、人間の目という高性能な異常検知器に情報を渡すための手段だと考えるのが適切です。検証の主力は理論値との突き合わせと保存則であって、アニメーションは補助的な位置づけになります。

コードレビューと単体テストは、ソフトウェア開発の一般的な手法をそのまま持ち込むものです。シミュレーションのコードは、分布から乱数を引く部分、時計を進める部分、資源を管理する部分、統計を集計する部分に分解できます。それぞれを独立した関数にしておけば、入力と期待出力を固定した自動テストが書けます。とくに集計部分は、待ち時間の平均や信頼区間の計算を、手で計算できる小さな配列で確かめておく価値があります。集計の誤りは、モデル本体が正しくても最終的な数字を狂わせるためです。

妥当性確認の手法、現実を十分に写しているか

妥当性確認の手法については、Sargentによる整理として一般に知られている分類があります。ここではその分類に沿って、実務で使いやすいものを取り上げます。いずれも「モデルが正しいことを証明する」ものではなく、「目的に対して十分でない兆候を探す」ものだと理解しておくのが適切です。

フェイスバリディティは、現場の熟練者にモデルの構造と結果を見せて、違和感を聞く方法です。表面的な妥当性という意味の言葉で、専門的な分析をせずに、見た目の説得力を専門家の判断に委ねます。手法としては最も素朴ですが、実務では最も効率がよい部類に入ります。工程の順序、例外処理の存在、繁忙期の運用の切り替えといった、データには現れないが結論を左右する事柄は、現場の頭の中にしかないためです。

聞き方には工夫が要ります。「このモデルは妥当だと思いますか」と尋ねても、有用な答えは返ってきません。判断の根拠を相手に丸投げしているためです。「この工程で、実際には1日に何回くらい割り込みが入りますか」「この数字が現場の実感と違うとしたら、どこが原因だと思いますか」というように、具体的な事実を聞く形にします。モデルの構造を図で示し、無視した要素を明示的に列挙して、そこに重要なものが混じっていないかを確認するのも効果的です。

過去データとの照合は、過去の実績期間の条件をモデルに入力し、そのときの実績を再現できるかを見る方法です。到着の実績、稼働の実績、要員配置の実績を入力として与え、出力される待ち時間や処理量が、実際に記録された値と近いかを比べます。数量的な裏づけが取れるという点で、フェイスバリディティより強い証拠になります。ただし後述するように、当てに行くと意味を失うという落とし穴があります。

感度分析は、入力を動かしたときの反応の向きと大きさが、現場の直感と一致するかを見る方法です。要員を1人減らしたら待ち時間はどれくらい伸びるか、到着が1割増えたらどうなるか、故障率を倍にしたらどうなるか。向きが逆であれば実装か前提が誤っています。向きが合っていても、大きさが現場の感覚と桁違いであれば、どちらかが誤っているか、現場が気づいていない非線形性があります。第5章で見たように、利用率が高い領域では待ち時間が非線形に伸びるため、現場の直感と食い違うこと自体が発見になる場合もあります。

チューリングテスト的な照合は、モデルの出力と実績の出力を混ぜて現場の人に見せ、どれがモデルの結果かを区別できるか試す方法です。区別できなければ、少なくともその指標については実績と見分けがつかない水準にあるといえます。区別できた場合は、なぜ区別できたのかを聞くことに価値があります。「実績ではこの時間帯にこんなに空くことはない」といった指摘が、モデルに欠けている要素を教えてくれます。

事象の妥当性は、実績で起きた事象がモデルでも起きるかを確かめる方法です。平均値が合っていることと、特徴的な出来事が再現されることは別です。月末に処理が滞留する、特定の品目が入ると段取り替えで長時間止まる、一定以上の行列ができると顧客が離脱する。こうした事象が実績で観測されているなら、モデルでも同じ頻度で起きるべきです。平均だけを合わせたモデルは、こうした事象を一度も起こさないまま、それらしい平均値を出すことがあります。

手法何をするか必要なもの強さと限界
フェイスバリディティ構造と結果を熟練者に見せ、違和感を具体的に聞く現場担当者の時間、構造を示す図データにない前提を拾える。定量的な保証はない
過去データとの照合過去の期間を入力し、実績を再現できるか比べる入力と結果が対になった実績データ定量的な証拠になる。合わせ込みの危険がある
感度分析入力を動かし、反応の向きと大きさを現場の直感と照らす実行時間、現場の判断構造の誤りを見つけやすい。絶対水準は保証しない
チューリングテスト的照合モデルの出力と実績を混ぜ、区別できるか試す実績の出力、判定に協力する担当者見分けがつかないことを示せる。指標を選ぶ必要がある
事象の妥当性実績で起きた特徴的な事象がモデルでも起きるか確かめる事象の記録、発生頻度平均が合うだけでは分からない欠落を拾える
内部妥当性の確認同じ条件で反復したときのばらつきの大きさを見る複数回の実行ばらつきが過大なら構造か入力を疑える
極端条件の妥当性極端な条件での挙動が現実的にあり得るか判断する現場の知識外挿の危険な領域を早期に見つけられる

過去データとの照合、当てに行かないという原則

過去データとの照合は最も説得力のある妥当性確認ですが、やり方を誤ると逆に危険なモデルを作ります。危険なのは、実績に合うまでパラメータを調整してしまう場合です。故障率を少し上げ、段取り時間を少し延ばし、離脱の閾値を少し下げれば、たいていのモデルは過去の実績に合わせられます。合ったモデルは説明会では歓迎されますが、将来の条件では外れます。

この問題は、機械学習でいう過適合と同じ構造です。調整できるパラメータが多いほど、過去への当てはまりはよくなり、未知の条件への対応力は落ちます。シミュレーションモデルは、分布のパラメータ、ルールの閾値、確率的な分岐の比率など、調整できる箇所が多数あります。合わせようと思えばいくらでも合わせられるという性質を、作る側が自覚しておく必要があります。

対策は、機械学習と同じく、独立した期間で試すことです。実績データを期間で分け、前半の期間を使って入力の分布やルールを決め、後半の期間はいっさい見ないでおきます。モデルが固まってから後半の期間の条件を入力し、そのときの実績と比べます。後半で外れたら、外れた事実を記録します。後半に合わせて調整し直すと、後半も合わせ込みの対象になってしまうため、その時点で独立した検証材料を失います。

期間の分け方には注意が要ります。単純に前半と後半で切ると、季節性や制度変更が片方だけに含まれることがあります。繁忙期と閑散期の両方が各期間に入るように分ける、あるいは複数の分け方を試して結果が変わらないことを見る、といった工夫が要ります。第8章で扱った入力データの期間の選び方と同じ問題が、ここでも出てきます。

照合の合格ラインは、目的から決めます。要員配置の判断に使うモデルであれば、待ち時間の絶対値が数分ずれていても、要員を1人増やしたときの改善幅の順序が実績と整合していれば十分な場合があります。逆に、契約上の応答時間を保証できるかを判断するモデルであれば、絶対値の精度が要ります。何パーセント以内なら合格かを事前に決めておき、実行して結果を見てから基準を緩めることをしない、という運用が要ります。

合わなかった場合の扱いも決めておきます。合わなかったという事実は、隠すのではなく記録します。どの期間の、どの指標が、どれだけ外れたか。原因として何を疑い、何を調べ、何が分かったか。原因が特定できないまま残ったなら、その旨を限界として文書に残します。合わなかった記録が残っているモデルのほうが、すべて合ったと書いてあるモデルより信頼できます。後者は、合わない条件を試していないだけである可能性が高いためです。

モデルは正しくない、有用かどうかだけが問題

統計学の分野で古くから言われてきた考え方に、すべてのモデルは誤っているが一部は有用である、というものがあります。シミュレーションにもそのまま当てはまります。現実の工場には、モデルに書かれていない要素が無数にあります。作業者の熟練度の差、天候、設備の個体差、その日の気分。これらをすべて写し取ることは原理的に不可能ですし、写し取ろうとすることに意味もありません。

したがって、妥当性確認の目標を「モデルが正しいことを示す」に置くと、永久に終わりません。目標は「目的に対して十分であることを示す」に置きます。この2つは似ているようで、作業量がまったく違います。前者は無限の検証を要求しますが、後者は目的が定まっていれば有限の作業で終わります。目的が定まっていないプロジェクトで妥当性確認が終わらないのは、多くの場合、技術の問題ではなく目的の問題です。

ここから、実務上きわめて有効な考え方が出てきます。正しさを証明しようとする代わりに、適用範囲を宣言するという方法です。適用範囲とは、このモデルが答えてよい問いの範囲のことです。どの指標について、どの条件の下で、どの程度の精度で答えられるか。そして、どの問いには答えられないか。これを文書として明示的に書きます。

たとえば、ある物流拠点のモデルについて、適用範囲を次のように書けます。日次の出荷量が3,000件から8,000件の範囲において、ピッキング要員数を10名から30名の範囲で変えたときの、出荷完了時刻の比較に用いる。絶対値の誤差は実績比で1割程度あるため、絶対時刻の保証には用いない。取扱品目の構成が現行から大きく変わる場合、および夜間帯の運用を新設する場合は範囲外である。

このように書いておくと、いくつかの効果があります。第一に、範囲内の問いについては、限られた検証で信頼を主張できます。第二に、範囲外の問いを持ち込まれたときに、断る根拠になります。第三に、範囲を広げたいという要望が出てきたときに、追加で何を検証すべきかが具体的に決まります。正しさの証明より、この宣言のほうが実務的な価値が高いと考えています。

範囲外の使い方が最も危険なのは、検証していない条件で外挿して意思決定に使う場合です。利用率0.7から0.85の範囲で妥当性を確認したモデルを、利用率0.97の条件に入れて答えを出すと、数字自体は出ます。第5章で見たとおり、この領域では待ち時間が急激に伸びるため、モデルの小さな前提の違いが結果を大きく変えます。範囲外であることを明示していなければ、出てきた数字はそのまま判断に使われます。

信頼を作る進め方と、よくある失敗

信頼を作る進め方の第一は、小さく作って早く見せることです。すべての工程を含む完全なモデルを3か月かけて作ってから見せると、最初の指摘で3か月分が崩れます。主要な1工程だけのモデルを1週間で作り、現場に見せて指摘を受け、直してからもう1工程足す。この進め方であれば、指摘によって崩れる量が常に小さく保たれます。現場の側も、途中から関わったものより、最初から見てきたもののほうを信じます。

第二は、現場の人を検証に巻き込むことです。完成品を見せて意見を求めるのではなく、検証の作業そのものに参加してもらいます。トレースの出力を一緒に読む、アニメーションを一緒に見る、極端な条件での結果を一緒に予想してから実行する。参加した人は、そのモデルがどこまで確かめられているかを自分の目で見ているため、後になって根拠のない疑いを持ちにくくなります。

第三は、モデルが外した事例を隠さず記録することです。予測と実績がずれた案件を一覧にして、いつ、どの条件で、どれだけずれたかを残します。この一覧があると、次に似た条件の判断をするときに、どれくらいの幅を見ておくべきかが分かります。外した記録を持っているモデルは、精度そのものは変わらなくても、使う側にとっての価値が高くなります。

第四は、数字より先に構造の合意を取ることです。どの範囲を切り出すか、何を1つのエンティティとするか、どの資源を明示的に扱い、どれを平均的な遅れとして丸めるか。これらを図に描いて合意してから、数字を作ります。構造に合意していない状態で数字を出すと、数字への反論という形で構造の議論が始まり、話が戻ります。

次に、実務でよく見る失敗を整理します。第一は、精度を上げるために粒度を上げ続けて完成しないという失敗です。ずれが見つかるたびに要素を追加していくと、モデルは重くなり、入力データの要求も増え、いつまでも運用に入りません。粒度は目的から決めるものであって、ずれの大きさから決めるものではありません。ずれが許容範囲に入っているなら、そこで止めます。

第二は、現場が知らないうちに作られたモデルが、正しくても使われないという失敗です。技術的には妥当性確認まで済んでいても、現場から見れば、自分たちの仕事を外部の誰かが数式にしたものです。運用に組み込まれるかどうかは、正しさとは別の要因で決まります。第三は、過去データに合わせ込みすぎて将来に使えないという失敗で、前節で扱ったとおりです。

第四は、検証していない条件で外挿して意思決定に使う失敗です。適用範囲の宣言がないモデル、あるいは宣言があっても運用時に参照されないモデルで起きます。第五は、モデルを作った人しか動かせず、その人が異動すると使えなくなるという失敗です。属人化はシミュレーションに限らない問題ですが、シミュレーションは前提の塊であるため、前提が文書化されていないと、コードが残っていても意味を読み取れません。

失敗の型起きる理由予防策
粒度を上げ続けて完成しない粒度を目的ではなくずれの大きさで決めている目的から粒度と合格ラインを事前に決める
正しくても使われない現場が作る過程に関わっていない小さく作って早く見せ、検証に参加してもらう
過去に合わせ込んで将来に使えない調整できる箇所が多く、合わせられてしまう独立した期間で試し、調整に使わない
検証していない条件で外挿する適用範囲が宣言されていない、参照されていない範囲を文書で宣言し、実行時に範囲外を警告する
作った人しか動かせない前提と手順が本人の頭の中にしかない仮定一覧と実行手順を文書化し、別の人に実行させて確かめる

文書化の最低ライン

ここまでの内容は、記録に残らなければ次の担当者に引き継がれません。何を書き残すべきかを、最低ラインとして具体的に挙げます。分量を増やすことが目的ではないので、各項目は数行で構いません。書いていない項目がないことのほうが重要です。

第一に、目的と答える問いです。このモデルは何を決めるために作られたのか、具体的にどんな問いに答えるのかを書きます。「工場の生産性向上のため」のような書き方では役に立ちません。「A工程の要員を2名増やしたときに、日次の出荷完了時刻が何分早まるかを見積もる」のように、答えの形が決まる書き方にします。

第二に、適用範囲と範囲外です。前節で扱ったとおり、どの条件の範囲で、どの指標について、どの精度で答えられるかを書きます。あわせて、明示的に範囲外とする問いを列挙します。範囲外の列挙は、書き手にとっては気が進まない作業ですが、後で誤用を防ぐ効果が最も大きい項目です。

第三に、仮定の一覧です。モデルを作る過程で置いた仮定を、すべて箇条書きにします。待ち行列は先着順である、故障は独立に発生する、作業者の熟練度は考慮しない、昼休みは全員一斉である。書き出してみると、意識せずに置いていた仮定が必ず出てきます。この一覧は、フェイスバリディティで現場に見せる材料としても使えます。

第四に、入力データの出所と期間です。どのシステムの、どのテーブルから、いつからいつまでのデータを取ったか。除外した外れ値や欠損の扱いをどうしたか。第8章で扱った分布の当てはめについて、どの分布を選び、どの検定を通したかも含めます。データの出所が書かれていないモデルは、数年後に再現できません。

第五に、乱数の種です。どの実行でどの種を使ったかを記録しておけば、後から同じ結果を再現できます。再現できることは、誤りの調査でも、結果に疑問が出たときの確認でも要になります。第11章で扱う共通乱数法を使う場合は、比較する案の間で種をどう揃えたかも書きます。

第六に、検証と妥当性確認の記録です。本章で挙げた手法のうち、どれを実施し、何が分かったかを書きます。理論値との突き合わせであれば、条件と理論値と推定値と信頼区間を残します。合わなかった項目、実施しなかった項目も書きます。実施しなかった項目については、なぜ実施しなかったかを一行添えます。

第七に、既知の限界です。現時点で分かっている弱点を書きます。特定の条件で実績と合わないこと、扱っていない要素があること、データの期間が短いこと。限界を書くと信頼を損なうように思えますが、実際には逆です。限界が書かれていないモデルは、限界を調べていないモデルだと受け取られます。

これらの文書は、作って終わりではありません。モデルは運用の中で改修され、前提は変わっていきます。改修のたびに文書を更新し、いつ誰が何を変えたかを残す運用が要ります。この運用の仕組み、つまりモデルを台帳として管理し、引き継げる形で保つ方法については第16章で扱います。本章で作った文書は、その台帳に載せる中身そのものになります。

文書化の最低ラインを表す一覧図

この章を深めたい方への参考書籍

Simulation Modeling and Analysis 第6版(Averill M. Law、McGraw-Hill)
シミュレーションの標準的な参考書です。妥当なモデルの作り方、出力データの分析、代替案の比較、分散減少法、実験計画と最適化がそれぞれ独立した章として扱われており、本コラムの後半の各章に対応します。

第11章 分散減少法と実験計画、少ない計算で差を見分ける

第9章で、シミュレーションの結果は1本の数値ではなく統計的な推定値であり、点推定と区間をセットで扱う必要があることを確かめました。そこで得られた実務上の帰結は、必要な精度を先に決めれば必要な反復回数が決まる、というものでした。区間の幅は反復回数の平方根に反比例するので、幅を半分にしたければ反復回数は4倍必要になります。この関係は避けようがなく、精度を求めるほど計算量が増えます。

ところが計算資源は有限です。1回の実行が1秒で終わるモデルなら1000回まわしても20分足らずですが、工場全体を1日分動かすモデルや、数十万エージェントを扱うモデルでは、1回の実行に数分から数十分かかることも珍しくありません。そこに反復回数を4倍と言われれば、一晩では終わらなくなります。案が2つならまだしも、比べたい案が20通りあれば、そのまま20倍です。

この章で扱うのは、その制約のもとで何ができるかという問題です。取れる手は大きく2つあります。1つは、推定値のばらつきそのものを小さくして、同じ反復回数でより狭い区間を出すこと。これを分散減少法と呼びます。もう1つは、比べたい条件の組み合わせを賢く選び、少ない実験回数で多くのことを読み取ること。こちらが実験計画の話です。前者は1つの比較を鋭くする道具、後者は比較の本数そのものを減らす道具で、どちらも「同じ計算量でどれだけ多くを知るか」という同じ目的に向かっています。

この2つは、統計学の側から見れば新しい話ではありません。分散減少はモンテカルロ法の古典的な工夫であり、実験計画は農事試験や工業実験のために整えられた体系です。シミュレーションが特殊なのは、実験の条件を完全に制御でき、乱数まで自分の手で決められる点にあります。現実の実験ではどうやっても揃えられない条件が、シミュレーションでは揃えられます。その自由度をきちんと使うかどうかで、必要な計算時間が倍以上変わります。

差のばらつきが、判定の壁になっている

まず、何が問題なのかをはっきりさせます。案Aと案Bを比べたいとします。知りたいのは、たとえば平均待ち時間が案Bで何分短くなるかという差です。素直な進め方は、案Aを200回反復して平均を求め、案Bも200回反復して平均を求め、その差を取る、というものです。

このとき差の推定値もばらつきます。案Aの推定値がばらつき、案Bの推定値もばらつくので、その差はもっとばらつきます。統計の基本的な関係で書けば、2つの量の差の分散は \( \mathrm{Var}(X_A - X_B) = \mathrm{Var}(X_A) + \mathrm{Var}(X_B) - 2\,\mathrm{Cov}(X_A, X_B) \) です。最後の項の \( \mathrm{Cov} \) は共分散といい、2つの結果が同じ方向に動く度合いを表す数値です。片方が大きいときにもう片方も大きくなる関係が強いほど、この値は大きくなります。案Aと案Bを別々の乱数で動かすと、両者の結果は互いに無関係になるので共分散の項が0になり、差の分散は2つの分散の単純な和になります。つまり、差を見ようとした瞬間に、ばらつきは片方だけを見ていたときより大きくなります。

ここが実務での壁になります。案Bの改善効果が大きければ問題ありません。待ち時間が10分から3分に減るなら、多少ばらついても差は明らかです。困るのは効果が小さいときで、たとえば0.5分の短縮を判定したいのに、差の区間の幅が1.0分あると、区間が0をまたいでしまい「差があるとは言えない」という結論しか出せません。しかも実務で判断が割れるのは、たいてい効果が小さくて微妙な案です。効果が大きい案は誰が見ても採用なので、そもそも精密な比較を必要としません。

反復回数を増やせば区間は狭まります。しかし必要な反復回数は、目標とする半幅の2乗に反比例し、差の分散に比例します。式で書けば、必要な反復回数はおよそ \( n \approx \left( \dfrac{z \cdot \sigma_d}{h} \right)^2 \) です。ここで \( \sigma_d \) は差の標準偏差、\( h \) は目標の半幅、\( z \) は信頼水準に対応する値です。この式の読み方は2通りあります。半幅 \( h \) を小さくしたいなら \( n \) を増やす、というのが第9章までの読み方でした。もう1つの読み方は、分子の \( \sigma_d \) を小さくできれば、同じ \( h \) をより小さい \( n \) で達成できる、というものです。

先ほどの分散の式に戻ると、\( \sigma_d \) を小さくする道が1つ見えています。共分散の項です。案Aと案Bの結果に正の相関を作れば、\( -2\,\mathrm{Cov}(X_A, X_B) \) の分だけ差の分散が減ります。相関が強いほど差の分散は小さくなります。案ごとの結果のばらつきを減らすのではなく、2つの結果を連動させることで、差だけを鋭く見る。これが本章の主役である手法の原理です。

共通乱数法、同じ条件のもとで案を比べる

共通乱数法は、比べたい複数の案に対して同じ乱数の系列を使う方法です。案Aを動かすときに使った乱数の並びを、案Bでもそのまま使います。そうすると、両方の案が同じ客の並び、同じ需要の推移、同じ故障の発生時刻のもとで評価されます。案の違い以外の条件が完全に揃うので、出てきた差は案の違いだけに由来する、と言えるようになります。

現実の実験にたとえると分かりやすくなります。2種類の靴の履き心地を比べたいとして、100人にA、別の100人にBを履いてもらう方法と、同じ100人に両方を履いてもらう方法があります。前者では、たまたまAのグループに足の大きい人が集まったといった個人差が結果に混ざります。後者では、同じ人の中でAとBを比べるので、個人差は差を取った時点で消えます。統計ではこれを対応のある比較と呼び、同じ人数でも検出力が高くなることが知られています。共通乱数法は、シミュレーションでこの「対応のある比較」を作る手続きにあたります。

シミュレーションが現実の実験より有利なのは、この対応づけを完璧にできる点です。同じ人に両方の靴を履いてもらう場合でも、1足目を履いた疲れが2足目に影響するといった順序効果が残ります。シミュレーションには疲れがありません。同じ乱数を渡せば、案Aのときの第37番目の客と案Bのときの第37番目の客は、寸分違わず同じ時刻に来て同じ量の作業を持ってきます。現実の実験では絶対に作れない条件が、乱数の管理だけで手に入ります。

この方法が効かない場合もあります。効果の源泉は案Aと案Bの結果のあいだの正の相関なので、相関が生まれない構造では効きません。極端な例として、案Aと案Bで使う乱数の役割がまったく違ってしまう場合、たとえば案Aは在庫方策を変えるだけなのに案Bは商品構成そのものを変えるといった場合、同じ乱数を渡しても対応がつきません。また、まれに負の相関が生じて差の分散が増えることも理論上はありえます。実務でよく比べるような、資源の数・優先順位・レイアウトといった運用条件の違いでは、正の相関が出るのが普通です。判断に迷うなら、独立な乱数の場合と共通乱数の場合を両方走らせて差の標準偏差を比べれば、効いているかどうかはすぐ分かります。

もう1つ注意すべきなのは、共通乱数法は差の推定を鋭くする方法であって、案A単独の推定値を鋭くするわけではないという点です。案Aの平均待ち時間そのものを精密に知りたいなら、その分の反復回数は別に必要です。実務で本当に知りたいのが「案Bにすると何分短くなるか」であるなら共通乱数法は強力ですが、「案Bにすると何分になるか」を知りたいなら通常の反復が必要になります。何を推定したいのかを先に決めておくべき理由の1つです。

実装の勘所、乱数の流れを用途ごとに分ける

共通乱数法は考え方が単純なので、両方の実行に同じ乱数の種を渡せば済むと思われがちです。ところが、それだけでは効かないことがあります。むしろ、素朴に同じ種を渡す実装は失敗しやすい部類に入ります。理由は、乱数が「消費される順序」にあります。

離散イベントシミュレーションでは、乱数生成器から必要になったつど値を引きます。客が到着するときに到着間隔用の乱数を1つ引き、サービスが始まるときにサービス時間用の乱数を1つ引く、という具合です。1本の乱数の流れをすべての用途で共有していると、引かれる順序はイベントの発生順序に依存します。ここで窓口の数を4人から5人に変えると、サービスが終わる時刻が変わり、イベントの順序が変わり、乱数の消費順序がずれます。

ずれた結果どうなるかというと、案Aでは第37番目に引かれた乱数が到着間隔に使われ、案Bでは同じ乱数がサービス時間に使われる、という事態が起きます。こうなると、同じ種を使っているにもかかわらず、案Aと案Bはまったく別の世界を再現していることになります。相関は作れず、共通乱数法の効果は消えます。しかも実装した側からは、同じ種を渡しているので効いているように見えてしまいます。この状態を同期の崩れと呼びます。

対策は2段構えです。1つ目は、乱数の流れを用途ごとに分けることです。到着間隔用の生成器、サービス時間用の生成器、故障発生用の生成器、というように独立した生成器を用意し、それぞれに別の種を与えます。こうすると、サービス時間を何回引いたかが到着間隔の系列に影響しません。この分離をストリームの分離と呼びます。

2つ目は、引く順序そのものを案に依存させないことです。もっとも確実なのは、実行の前に必要な乱数をまとめて生成しておき、客ごとに番号を振って配列から引く設計にすることです。第37番目の客はいつでも配列の37番目の値を使うので、窓口の数が何人であろうと、第37番目の客の作業量は同じになります。イベントの順序に一切左右されない対応づけが作れます。

この2つを実装したのが、次のコードです。到着間隔とサービス時間で生成器を分け、それぞれ2000個ぶんを事前に配列として作り、客の番号で引いています。窓口4人体制と5人体制を比べる例で、独立な乱数を使った場合と共通乱数法を使った場合の両方を計算しています。

import numpy as np
import simpy

OPEN_MIN, N_REP = 480, 200
ARRIVAL_RATE, SERVICE_MEAN = 0.9, 3.0

def run(n_staff, seed_arrival, seed_service):
    """乱数の流れを到着用とサービス用に分け、客ごとに番号で引く。
    こうすると窓口数を変えても、同じ種なら同じ客が同じ間隔で来て同じ作業量を持つ。"""
    ra = np.random.default_rng(seed_arrival)
    rs = np.random.default_rng(seed_service)
    gaps = ra.exponential(1 / ARRIVAL_RATE, 2000)
    svc = rs.exponential(SERVICE_MEAN, 2000)

    env = simpy.Environment()
    staff = simpy.Resource(env, capacity=n_staff)
    waits = []

    def customer(env, i):
        t0 = env.now
        with staff.request() as req:
            yield req
            waits.append(env.now - t0)
            yield env.timeout(svc[i])

    def source(env):
        for i in range(2000):
            yield env.timeout(gaps[i])
            if env.now > OPEN_MIN:
                return
            env.process(customer(env, i))

    env.process(source(env))
    env.run()
    return float(np.mean(waits))

# 独立な乱数:4人体制と5人体制で別々の種を使う
ind = np.array([[run(4, s, 5000 + s), run(5, 7000 + s, 9000 + s)] for s in range(N_REP)])
# 共通乱数法:同じ種を両方に使う(同じ客・同じ作業量で比べる)
crn = np.array([[run(4, s, 5000 + s), run(5, s, 5000 + s)] for s in range(N_REP)])

for name, arr in (("独立な乱数", ind), ("共通乱数法", crn)):
    d = arr[:, 0] - arr[:, 1]
    half = 1.96 * d.std(ddof=1) / np.sqrt(N_REP)
    print("%s: 短縮効果 %.3f分  95%%区間 [%.3f, %.3f]  幅 %.3f"
          % (name, d.mean(), d.mean() - half, d.mean() + half, 2 * half))

d_ind = ind[:, 0] - ind[:, 1]
d_crn = crn[:, 0] - crn[:, 1]
print("差の標準偏差 : 独立 %.3f → 共通乱数 %.3f" % (d_ind.std(ddof=1), d_crn.std(ddof=1)))
print("同じ精度を出すのに必要な反復回数の比 :",
      round((d_crn.std(ddof=1) / d_ind.std(ddof=1)) ** 2, 3))

コードの中で共通乱数法の本体にあたるのは、下から数えて数行のところにある2つの配列の作り方の違いだけです。独立な乱数では4人体制に種 s5000 + s、5人体制に 7000 + s9000 + s という別々の種を渡しています。共通乱数法では、どちらの体制にも同じ s5000 + s を渡します。モデル本体は一切変えていません。変えたのは種の渡し方だけです。

窓口4人と5人を比べる、反復回数が半分になる

実行結果は次のとおりです。

独立な乱数: 短縮効果 0.570分  95%区間 [0.517, 0.623]  幅 0.106
共通乱数法: 短縮効果 0.584分  95%区間 [0.546, 0.621]  幅 0.075
差の標準偏差 : 独立 0.381 → 共通乱数 0.271
同じ精度を出すのに必要な反復回数の比 : 0.506
比べ方短縮効果の推定値95%区間区間幅差の標準偏差
独立な乱数0.570分0.517から0.6230.1060.381
共通乱数法0.584分0.546から0.6210.0750.271
ほぼ同じ同じ範囲を含む0.71倍0.71倍

まず確かめておきたいのは、推定値そのものは大きく変わっていないことです。短縮効果は独立な乱数で0.570分、共通乱数法で0.584分でした。どちらも同じ量を推定しており、共通乱数法は推定値を都合よくずらす方法ではありません。区間もお互いの推定値を含む範囲にあります。分散減少法が変えるのは精度であって、推定の中身ではありません。

変わったのは幅です。95%区間の幅は0.106から0.075へ縮みました。差の標準偏差で見ると0.381から0.271で、およそ0.71倍です。反復回数は200回のまま、モデルも同じ、計算時間もほぼ同じで、区間だけが3割ほど狭くなっています。

この効果を計算時間の言葉に翻訳したのが最後の行です。必要な反復回数は差の分散に比例するので、標準偏差が0.71倍になれば分散は0.71の2乗でおよそ0.506倍になります。つまり、独立な乱数で200回まわして得られる精度は、共通乱数法ならおよそ101回で得られます。実行時間はほぼ半分です。1回の実行に10分かかるモデルで200回まわすと33時間ですが、101回なら17時間で済みます。翌朝に結果が出るかどうかの差になります。

この例で得られた0.506という比は、問題の構造によって変わります。案の違いが小さく、乱数の対応がきれいに取れる比較ほど相関が強くなり、削減効果も大きくなります。文献や実務では、条件がよければ必要な反復回数が数分の1から10分の1になる例も報告されています。逆に、対応がうまく取れない構造では効果がほとんど出ません。重要なのは、この効果がモデルの作り替えではなく乱数の管理だけで得られることです。追加の計算コストは実質ゼロで、実装の手間も生成器を分けて配列に置くだけです。費用対効果としては、シミュレーションの実務で最初に検討すべき工夫にあたります。

独立な乱数と共通乱数法で

ほかの分散減少法、どんな場面で効くか

分散減少法は共通乱数法だけではありません。目的によって使い分けるべき手法がいくつかあり、それぞれ効く場面がはっきり分かれています。共通乱数法が「2つ以上の案を比べる」場面の道具であるのに対し、以下の3つはどちらかというと「1つの量を精度よく推定する」場面の道具です。

対称変量法は、乱数とその裏返しを対にして使う方法です。0から1のあいだの一様乱数 \( u \) を引いたら、同時に \( 1-u \) も使い、両方の結果を平均します。\( u \) が大きめに偏った回は結果が一方向にずれますが、\( 1-u \) を使った回は逆方向にずれるので、平均を取ると偏りが打ち消し合います。効くのは、乱数の値と出力とのあいだに単調な関係があるときです。サービス時間の乱数が大きいほど待ち時間が長くなる、といった素直な関係が成り立つ場合には、対にした2回の結果が負の相関を持ち、平均のばらつきが小さくなります。逆に、乱数が大きいと結果が大きくなったり小さくなったりする複雑な関係では、打ち消しが起きず効果が出ません。プロジェクト工期のモンテカルロ評価のように、入力が大きいほど出力も大きいという構造がはっきりしている問題と相性が良い方法です。

制御変量法は、理論値が分かっている量との相関を利用して補正する方法です。シミュレーションの中には、答えが数式で分かっている量が混ざっていることがあります。たとえばサービス時間を平均3分の指数分布で発生させているなら、実際に発生した値の平均は理論上3分になるはずです。ある実行で発生したサービス時間の平均がたまたま3.2分だったなら、その実行は「重い客が多めに来た回」であり、待ち時間も長めに出ているはずだと推測できます。この関係を使って、待ち時間の推定値を下方に補正します。補正の量は、待ち時間とサービス時間平均の相関から統計的に決めます。効くのは、理論値が分かっていて、かつ知りたい量と強く相関する補助変数が見つかるときです。待ち行列の問題では到着数やサービス時間の平均が使えますし、金融のリスク評価では単純化した近似モデルの解が補助変数になります。補助変数の理論値が本当に正しいことが前提なので、その確認を怠ると精度の高い誤りを作ります。

重点サンプリングは、めったに起きない事象を意図的に多く起こして、重みで補正する方法です。年に1回しか起きない設備故障の影響を評価したい場合、素直に動かすと1000回まわして数回しか故障が起きず、その数回の結果に推定が振り回されます。そこで故障確率を人為的に大きくした分布から乱数を発生させ、本来の確率との比を重みとして結果に掛けます。重みで戻すので推定値は偏りませんが、故障が起きた回のサンプルが大量に得られるため、ばらつきが劇的に小さくなります。効くのは、まれな事象そのものが評価対象になっている場面です。保険の巨大損失、通信網の輻輳、安全性評価における事故確率、在庫における欠品といった、確率が小さく影響が大きい事象の評価に向いています。ただし、どう分布を歪めるかの設計が難しく、下手に選ぶと重みが極端に大きい少数のサンプルに支配されて、かえってばらつきが増えます。4つの中では実装難度がもっとも高い部類です。

手法原理効く場面主な注意点
共通乱数法案どうしに同じ乱数を使い、正の相関を作って差の分散を減らす2つ以上の案の優劣を比べるとき乱数の流れを用途別に分けないと同期が崩れて効かない
対称変量法乱数とその裏返しを対にして平均し、偏りを打ち消す入力と出力に単調な関係がある推定関係が単調でないと効果が出ない
制御変量法理論値の分かる量とのずれで推定値を補正する補助変数の理論値が正確に分かるとき理論値が誤っていると精度よく誤る
重点サンプリングまれな事象を多く起こし、重みで元に戻す低確率で影響の大きい事象の評価歪め方の設計が難しく、失敗すると逆効果

4つのうち、実務で最初に手を出すべきなのは共通乱数法です。実装が単純で、副作用が少なく、比較という目的が実務の関心にもっとも近いからです。次に検討する価値があるのは重点サンプリングですが、これはまれな事象を扱う必要が生じたときに限られます。対称変量法と制御変量法は、モンテカルロで1つの量を精密に推定したいときに追加で検討する位置づけになります。

案が増えると、比較の本数が爆発する

ここまでは案が2つの場合の話でした。実務ではそう単純にいきません。窓口を何人にするか、レイアウトをどう組むか、優先順位のルールをどうするか、シフトをどう割り当てるか、在庫の発注方策をどうするか。こうした要因が同時に並びます。

実験計画の言葉では、動かせる条件を要因、その要因が取る値を水準と呼びます。窓口数という要因が3人・4人・5人・6人という4つの水準を持つ、という言い方をします。要因が5つあり、それぞれ3水準だとすると、全組み合わせは \( 3^5 = 243 \) 通りです。各組み合わせについて200回反復するなら48,600回の実行が必要になります。1回10秒でも135時間です。要因を1つ追加すれば3倍に増えます。この増え方を組み合わせ爆発と呼び、実務でシミュレーションが「重くて使えない」と言われる原因の多くはここにあります。

そこで登場するのが実験計画法です。要因の組み合わせをすべて試すのではなく、統計的に設計された一部だけを試して、要因の効果を推定します。もともとは農事試験のために整えられた体系で、その後は工業製品の品質改善で広く使われました。シミュレーションでも考え方はそのまま使えます。むしろ、条件を完全に制御でき、実験の実施コストが計算時間だけで済むシミュレーションのほうが、実験計画の利点を素直に受け取れます。

実験計画に入る前に、多くの現場が自然に採用している進め方を検討しておきます。一度に1つの要因だけを動かし、良かった水準に固定してから次の要因に移る、という方法です。まず窓口数を3人から6人まで試して4人が良いと決め、次に4人に固定してレイアウトを3種類試し、良いものに固定して優先順位のルールを試す。直感的で説明もしやすく、実験回数も要因ごとの水準数の合計で済むので少なくなります。

この進め方には2つの弱点があります。1つ目が決定的で、要因どうしの交互作用が見えないことです。交互作用とは、ある要因の効果が別の要因の水準によって変わる関係を指します。たとえば、レイアウトが直線型のときは窓口を1人増やすと待ち時間が0.5分減るのに、レイアウトが島型のときは0.1分しか減らない、という状況です。この場合「窓口を1人増やす効果」という単一の答えは存在せず、レイアウトとセットでしか語れません。一度に1つだけ動かす方法では、最初に固定したレイアウトのもとでの窓口の効果しか測れないので、この関係を見落とします。

2つ目の弱点は効率です。一度に1つだけ動かす方法では、ある要因を評価している最中に得たデータが、他の要因の評価に使えません。後述する要因計画では、すべての実験点がすべての要因の効果推定に寄与するので、同じ実験回数でも各効果の推定精度が高くなります。少ない回数で済むように見える方法が、実は情報効率で劣っているという逆転が起きます。

2水準要因計画と一部実施要因計画、スクリーニングの二段構え

この節でこれから出てくる用語は数が多いのですが、言っていることは1つです。全部の組み合わせは試さない。まず粗く回して効く要因だけを見つけ、絞ってから細かく調べる。以下は、その二段構えを実験の設計としてどう組むかという話です。

要因計画は、複数の要因を同時に動かす設計です。もっとも基本的なのが2水準要因計画で、各要因に低い水準と高い水準の2つだけを設定し、その全組み合わせを実行します。要因が \( k \) 個なら実験点は \( 2^k \) 個です。5要因なら32点で、3水準の全組み合わせ243点よりずっと少なくなります。

2水準に絞ることには理由があります。まず、探索の初期段階で知りたいのは「その要因が効くのか効かないのか」であって、最適な値そのものではありません。効くかどうかを判定するだけなら、離れた2点を比べれば十分です。次に、2水準に統一すると各要因の主効果と、要因どうしの交互作用を、単純な足し引きで推定できます。高い水準での平均から低い水準での平均を引けば主効果が出ます。交互作用も同様に符号を組み合わせた足し引きで出ます。計算が単純なので、要因が多くても扱えます。

それでも \( 2^k \) は要因が増えると膨らみます。10要因なら1024点です。ここで使うのが一部実施要因計画で、\( 2^k \) 点のうち規則的に選んだ一部、たとえば半分や8分の1だけを実行します。実験点を \( 2^{k-p} \) 個に減らすので、10要因を32点で調べるといったことが可能になります。

ただで済むわけではありません。実験点を減らすと、いくつかの効果が区別できなくなります。ある要因の主効果と、別の2要因の交互作用が、まったく同じ計算式で推定されてしまう状態が生じます。これを交絡と呼びます。どの効果とどの効果が交絡するかは設計によって決まっており、その程度を表す指標が解像度です。解像度が高い設計ほど、主効果が低次の交互作用と交絡しにくくなります。実務では、主効果が2要因交互作用と交絡しない設計を選んでおけば、初期の絞り込みには十分なことが多いといえます。

この性質を積極的に使うのがスクリーニングという考え方です。最初から精密な実験をするのではなく、二段構えで進めます。第1段では、要因を多めに並べ、水準を2つに絞り、一部実施要因計画で少ない回数だけ実行します。目的は最適解を見つけることではなく、効く要因と効かない要因を仕分けることです。多くの問題では、並べた要因のうち実際に大きく効くのは2つか3つで、残りは誤差の範囲に埋もれます。第2段では、生き残った少数の要因だけを取り出し、水準を細かく振り、交互作用も含めて詳しく調べます。要因の数が減っているので、全組み合わせを試しても回数が現実的な範囲に収まります。

進め方5要因のときの実験点数交互作用向いている段階
全組み合わせ、各要因3水準243点すべて推定できる要因が2つか3つに絞れた後
2水準要因計画32点すべて推定できる要因が5つ程度までの詳細調査
一部実施要因計画、半分16点一部が交絡する要因の絞り込み
一部実施要因計画、8分の14点多くが交絡する要因が10以上あるときの初期選別
一度に1つだけ動かす要因ごとの水準数の合計見えない推奨しない

実験計画には、ここで挙げた以外にも中心複合計画や直交表など多くの型があります。ただし実務のシミュレーションで最初に効くのは、全組み合わせをやめること、一度に1つだけ動かす進め方をやめること、そしてスクリーニングで要因を絞ってから細かく振ること、この3つです。細かい設計の型は、その3つを実行に移してから選べば間に合います。

スクリーニングの二段構えを示すフロー図

実務の順序と、精度では救えないもの

ここまでの道具を、実際のプロジェクトで使う順番に並べておきます。順番を守ることに意味があります。逆順で進めると、精密に測ってから測る必要のなかったことに気づく、という無駄が生じるためです。

  1. 動かせる要因を洗い出す。窓口数、レイアウト、優先順位のルール、シフト、在庫方策など、実際に変更できるものだけを挙げます。変更できない条件は要因ではなく前提条件として固定します。
  2. スクリーニングで効く要因を絞る。各要因を2水準にし、一部実施要因計画で少ない回数だけ実行します。この段階では反復回数も少なくてかまいません。効く要因は大きな差を出すので、粗い精度でも仕分けられます。
  3. 絞った要因で案を作り、共通乱数法で比較する。ここで初めて乱数の管理を丁寧に行います。生成器を用途別に分け、事前生成した配列を客や日付の番号で引く形にします。
  4. 必要な精度から反復回数を決める。まず少ない回数、たとえば20回ほど試して差の標準偏差を見積もり、目標とする半幅から必要な反復回数を逆算します。共通乱数法を使っているぶん、この見積もりは独立な乱数の場合より小さく出ます。
  5. 差の区間で結論を書く。「案Bのほうが良い」ではなく「案Bは平均待ち時間を0.584分短縮する。95%区間は0.546から0.621」と書きます。区間が0をまたいでいるなら、差があるとは言えないと書きます。

この順序で進めると、計算資源が必要な場所に集中します。スクリーニングの段階では反復回数を絞れるので、要因が多くても短時間で終わります。精度を必要とするのは最後の比較だけで、そこには共通乱数法が効いています。全体として、素朴に全組み合わせを高精度で回す場合の数十分の1の計算量で、同じ結論に到達できます。

もう1点、実務上の助言を加えておきます。ここで説明した設計と反復回数の決め方は、実行の前に文書に残しておくべきです。どの要因をどの水準で振ったか、乱数の種の範囲はいくつか、反復回数を何回にした根拠は何か。これを後から思い出すのは困難で、結果の再現ができなくなります。第10章で扱った妥当性確認の記録と同じ場所に置いておくと、モデルの信頼性を説明するときにまとめて使えます。

最後に、この章の道具の限界をはっきりさせておきます。分散減少法も実験計画も、推定の精度を上げる技術です。モデルそのものの誤りには何も作用しません。

この区別は重要です。サービス時間の分布を誤って指数分布と決めてしまったモデルがあるとします。第8章で確かめたとおり、平均が同じでもばらつきの形が違えば待ち時間は大きく変わり、一定なら2.015分、指数分布なら3.943分、変動係数1.5の対数正規分布なら6.386分になります。この誤った指数分布のモデルに共通乱数法を適用すれば、3.943分という誤った値を、より狭い区間で推定できるようになります。区間が狭いということは、報告書での説得力が増すということです。誤った答えが、より自信を持って提示されることになります。

同じことが実験計画にも当てはまります。要因の洗い出しの段階で、実際には大きく効いている条件をモデルに入れ忘れていれば、どれだけ精密な計画を組んでも、その条件の効果は出てきません。スクリーニングは並べた要因の中から効くものを選ぶ手続きであって、並んでいない要因を見つける手続きではありません。

したがって本章の道具は、第8章で扱った入力のモデリングと、第10章で扱った検証と妥当性確認の上に乗せて初めて意味を持ちます。順番としては、まずモデルが現実を写しているかを確かめ、入力の分布をデータから決め、そのうえで精度の議論に入ります。この順序を逆にすると、精度よく間違った答えを出すことになります。

実務でこの順序が崩れやすいのは、精度の議論のほうが取り組みやすいからだと考えられます。分散減少法の実装は数十行のコードで済み、効果が数値ではっきり出ます。一方、妥当性確認は現場のヒアリングと実績データの突き合わせが必要で、時間がかかるうえ、うまくいっても「特に問題はなかった」という結果しか残りません。作業としての手応えは前者のほうが大きくなります。手応えのある作業を先にやってしまう傾向は認識しておくべきものだと感じています。

次の第12章では、ここまでの道具を組み合わせて、候補を評価するだけでなく良い案を探しに行く方法を扱います。実験計画が「どの点を試すか」を事前に設計するのに対し、そこでは試した結果を見ながら次に試す点を決める、という進め方に踏み込みます。評価の精度を保証する仕組みとして、本章の分散減少法がそのまま部品として使われます。

この章を深めたい方への参考書籍

モンテカルロ法ハンドブック(Dirk P. Kroese・Thomas Taimre・Zdravko I. Botev 著、伏見正則・逆瀬川浩孝 監訳、朝倉書店)
モンテカルロ法を体系的にまとめた一冊です。本章で扱った試行と推定の背景を、数理の側から確かめたいときに向きます。

第12章 シミュレーション最適化、評価と探索を組み合わせる

第1章で、シミュレーションと数理最適化は答える問いが違うと整理しました。シミュレーションは「この案を採ったら、どういう結果になりそうか」に答える技術で、数理最適化は「与えた条件のもとで最も良い案は何か」に答える技術です。試す道具と、決める道具。役割が違うので、どちらが優れているという比較には意味がありません。

本章は、その2つを接続する章です。実務でシミュレーションを使い始めると、遅かれ早かれ同じ壁に当たります。3つの案を比べるところまでは順調に進むのに、「では、いちばん良い設定はどこですか」と聞かれた瞬間に手が止まる、という壁です。案を評価する道具は手元にあるのに、案を選ぶ手段がない。この壁の越え方が本章の主題になります。

結論を先に言えば、越え方は単純です。シミュレーションを、案を入れると値が返ってくる関数だとみなします。そして、その関数の外側に探索の手続きを置きます。探索が候補を出し、シミュレーションがその候補を評価し、評価の結果を見て探索が次の候補を出す。この往復を回して、良い案にたどり着こうとします。この枠組みをシミュレーション最適化と呼びます。

枠組み自体は素直ですが、素直に実行すると失敗します。シミュレーションという関数は、数理最適化が前提にしている関数とは性質が大きく違うためです。評価に時間がかかり、勾配が取れず、同じ点を評価しても毎回違う値が返ってきます。この3つの性質が、手法の選び方と、結果の読み方の両方を左右します。本章では、その性質を確認したうえで、探索の手法を並べ、実際に格子で探索したコードの結果を読み、最後に実務での注意点を整理します。

シミュレーションは案を評価するが、案を選んではくれない

まず、なぜ壁に当たるのかを具体的に見ておきます。案が3つなら壁はありません。3つとも十分な反復回数でまわし、第9章の手順で信頼区間を付け、差が区間幅より大きいかどうかを見て報告すれば終わりです。案が10個でも、1回の評価が数秒なら同じやり方が通ります。全部試して、全部並べればよい。

問題が起きるのは、案の数が急に増える場合です。増え方には典型的な2つのパターンがあります。1つは、方策のパラメータが連続量である場合です。安全在庫の水準、窓口の稼働開始しきい値、価格の刻み、待ち時間の上限。これらは実数なので、候補は原理的に無限にあります。刻み幅を細かくすればするほど候補が増え、どこまで細かく刻むべきかという判断まで必要になります。

もう1つは、複数のパラメータが組み合わさる場合です。パラメータが1つで候補が10個なら10通りですが、パラメータが4つあれば1万通りになります。倉庫のレイアウトで通路の位置を選ぶ、シフト表で時間帯ごとの人数を決める、複数拠点の在庫水準を同時に決めるといった問題は、この形になります。組み合わせの数は指数的に増えるので、パラメータが少し増えただけで全数探索が現実的でなくなります。

ここで、シミュレーションの立ち位置を確認しておきます。シミュレーションが提供しているのは、案を入力すると成績が出力される仕組みだけです。良い案がどちらの方向にあるかも、いまの案が最良からどれくらい離れているかも教えてくれません。地図のない土地で、指定した地点の標高だけを測ってくれる装置を持っている状態に近いと言えます。装置は正確でも、それだけでは最も低い地点は見つかりません。

そこで、装置の外側に探索の手続きを置きます。どの地点を測るか、測った結果を見て次にどこを測るか、どこで打ち切るか。この3つを決めるのが探索側の仕事です。シミュレーション最適化とは、評価の仕組みと探索の手続きを組み合わせて、限られた測定回数で十分に良い地点を見つける技術の総称だと考えてよいと思います。

用語として注意しておきたいのは、この分野では目的関数という言葉が2つの意味で使われることです。数理最適化では、目的関数は数式として書き下されたものを指します。シミュレーション最適化では、目的関数は「まわして測る手続き」そのものを指します。式は存在しません。あるのは、入力を与えると数字が返ってくるブラックボックスだけです。この違いが、次の節で扱う構造上の差につながります。

数理最適化との構造の違い、3重のハンディ

数理最適化が効率よく解を見つけられるのは、目的関数と制約の中身が分かっているからです。中身が分かっていると、探索に構造を使えます。線形計画であれば、最適解が実行可能領域の頂点に現れるという性質を使って、頂点だけを辿ればよいと分かります。連続最適化であれば、勾配を計算して下る方向を直接決められます。整数計画であれば、条件をいったん緩めた解きやすい問題を解いて「これ以上は良くならない」という上限を先に求め、その上限を使って、見込みのない候補の集まりをまとめて捨てられます。用語としては緩和問題、双対性、分枝限定法と呼ばれるものですが、詳細は数理最適化の専門書に譲ります。ここで押さえておきたいのは、評価していない領域について「ここには良い解がない」と言い切れるという一点です。

これらはすべて、式を眺めて得られる情報です。式があるから、まだ評価していない領域について「ここには良い解がない」と断定できます。数理最適化の効率は、この断定ができることに由来しています。

シミュレーション最適化では、この断定ができません。式がないので、評価していない点については何も言えないからです。そのうえで、さらに3つのハンディが重なります。

第1に、評価が高価です。1回の評価が1秒で済むこともあれば、大きな工場のモデルで数分かかることもあります。数理最適化の反復1回は多くの場合ミリ秒の単位ですから、桁が3つも4つも違います。したがって「とりあえず何万回か試す」という発想が通りません。何回評価できるかを最初に決めて、その予算の中で探索を設計することになります。

第2に、勾配がありません。パラメータを少し動かしたときに成績がどちらに動くかを、式から求められません。差分で近似することはできますが、そこには第3のハンディが効いてきます。

第3に、同じ点を評価しても値が違います。乱数で動いている以上、これは避けられません。パラメータを少し動かして成績が0.3改善したように見えても、その0.3が方策の効果なのか乱数のぶれなのかが、そのままでは区別できません。ぶれの大きさが差より大きければ、差分から求めた勾配の符号すら信用できないことになります。

この3つ目が最も厄介です。評価が高価なだけなら、時間をかければ済みます。勾配がないだけなら、差分で近似できます。しかし値がぶれる以上、差分の分子には常に雑音が混ざります。しかも、ぶれを小さくするには反復回数を増やすしかなく、反復回数を増やすと評価がさらに高価になります。3つのハンディは独立ではなく、互いを増幅する関係にあります。

観点数理最適化シミュレーション最適化
目的関数数式として書かれているまわして測る手続きそのもの
1回の評価コスト小さい。反復を大量に回せる大きい。評価回数が予算になる
勾配解析的に求められる場合が多い取れない。差分で近似するしかない
同じ入力に対する出力常に同じ値乱数によりぶれる
使える構造凸性、双対性、分枝限定、緩和ほぼない。滑らかさを仮定する程度
未評価領域の扱い「良い解はない」と断定して捨てられる断定できない。捨てるのは賭けになる
解の保証厳密解や最適性ギャップが示せる統計的な保証にとどまる
扱える現実の複雑さ定式化できる範囲に限られるまわせる範囲なら何でも入る

表の最終行は、ハンディの見返りにあたります。数理最適化は、定式化できないものを扱えません。故障の連鎖、作業者の裁量、混雑時のふるまいの変化といった要素を式に落とすのは難しく、落とせない場合は問題そのものを単純化して定式化に合わせることになります。シミュレーション最適化にはその制約がありません。まわせるモデルであれば、どんなに込み入った挙動でも評価関数に入ります。効率を捨てる代わりに、扱える対象の広さを得ているという関係です。

数理最適化とシミュレーション最適化の構造の違いを表す対比図

コードで見る、発注方策を格子で探索する

ここで実際に探索をまわします。題材は在庫の発注方策です。在庫の残りが発注点を下回ったら、目標在庫まで一気に補充するという運用を考えます。発注点を s、目標在庫を S と書き、この2つの数字の組で運用が決まる方式を (s, S) 方策と呼びます。倉庫や店舗の補充ルールとして広く使われている形です。

費用は3つに分かれます。在庫を持ち越すことによる保管費用、発注1回ごとにかかる事務や輸送の固定費用、そして品切れで売り損ねたことによる欠品費用です。s を高くすれば欠品は減りますが在庫が積み上がり、S を大きくすれば発注回数は減りますが1回あたりの在庫が増えます。3つの費用は互いに逆方向に動くので、合計を最小にする組み合わせが存在します。それを探すのがこの実験です。

設定は次のとおりです。1日の需要は平均20のポアソン分布に従い、発注してから届くまでに3日かかります。保管費用は在庫1個1日あたり1、発注費用は1回60、欠品費用は1個あたり8としました。365日を1回の試行とし、各方策について60回ずつまわして平均費用と95%区間の半幅を求めます。s は20から120まで20刻み、Ss より20大きいところから180大きいところまで20刻みで振り、格子状に全部評価します。

第11章で扱った共通乱数法を、ここで使っています。cost_of の中で乱数生成器を試行番号から作り直しているため、方策が違っても同じ試行番号なら同じ需要系列が使われます。つまり全ての方策が、まったく同じ365日ぶんの需要に対して競うことになります。方策どうしを比べるときに、需要のくじ運の違いが混ざらないようにするための工夫です。

import numpy as np

DAYS, N_REP = 365, 60
LEAD = 3
C_HOLD, C_ORDER, C_SHORT = 1.0, 60.0, 8.0

def cost_of(s, S, seed):
    rng = np.random.default_rng(seed)          # 共通乱数:方策が違っても需要系列は同じ
    demand = rng.poisson(20, DAYS)
    inv, pipeline = S, [0] * LEAD
    hold = order_n = short = 0
    for d in range(DAYS):
        inv += pipeline.pop(0)
        sold = min(inv, demand[d])
        short += demand[d] - sold
        inv -= sold
        hold += inv
        if inv + sum(pipeline) <= s:
            pipeline.append(S - inv - sum(pipeline))
            order_n += 1
        else:
            pipeline.append(0)
    return (C_HOLD * hold + C_ORDER * order_n + C_SHORT * short) / DAYS

best = None
rows = []
for s in range(20, 121, 20):
    for S in range(s + 20, s + 181, 20):
        c = np.array([cost_of(s, S, seed) for seed in range(N_REP)])
        half = 1.96 * c.std(ddof=1) / np.sqrt(N_REP)
        rows.append((s, S, c.mean(), half))
        if best is None or c.mean() < best[2]:
            best = (s, S, c.mean(), half)

rows.sort(key=lambda r: r[2])
print("%-6s %-6s %-12s %-14s" % ("s", "S", "1日あたり費用", "95%区間の半幅"))
for s, S, m, h in rows[:6]:
    print("%-6d %-6d %-12.2f %-14.2f" % (s, S, m, h))
print("\n探索した組み合わせ:", len(rows), "通り")
print("最良: s=%d, S=%d, 費用 %.2f ± %.2f" % best)

コードの構造を確認しておきます。cost_of が評価関数、つまり目的関数にあたります。方策の2つの数字と試行番号を受け取り、1日あたりの平均費用を返します。その外側にある二重ループが探索にあたります。今回は格子を順に舐めるだけの単純な探索ですが、ここを別の手続きに差し替えれば、そのまま他の探索手法になります。評価と探索が分離しているという構造が、この節の要点です。

実行結果は次のとおりです。上位6件と、探索した組み合わせの総数、最良の方策が出力されます。

s      S      1日あたり費用      95%区間の半幅
60     100    52.31        0.16
60     120    54.73        0.14
40     100    59.52        0.31
60     80     59.99        0.25
60     140    60.63        0.17
40     120    61.76        0.33

探索した組み合わせ: 54 通り
最良: s=60, S=100, 費用 52.31 ± 0.16

結果の読み方、順位ではなく差の大きさで語る

54通りを探索して、最良は発注点60、目標在庫100の組み合わせで、1日あたりの費用は52.31、95%区間の半幅は0.16でした。この数字をどう報告するかが、シミュレーション最適化における実質的な難所です。順位表をそのまま渡すのは、多くの場合、誤った印象を与えます。

まず、1位と2位を見ます。1位は52.31、2位は54.73で、差は2.42あります。それぞれの半幅は0.16と0.14ですから、差はぶれの大きさに比べて十分に大きいと言えます。この差は乱数のいたずらでは説明できません。1位を選んでよいという判断ができます。

次に、3位と4位を見ます。3位は59.52で半幅0.31、4位は59.99で半幅0.25です。差は0.47しかなく、2つの半幅を足した0.56より小さくなっています。この2つは、順位表の上では3位と4位という別の場所に並んでいますが、実質的には区別がついていません。次に同じ実験を別の乱数でまわせば、順位が入れ替わっても不思議ではない関係です。

ここから引き出せる原則は1つです。ノイズのある比較では、順位ではなく差の大きさで語ります。「3位より4位のほうが劣る」ではなく「3位と4位は、この反復回数では区別がつかない」と報告します。区別がつかないと分かったら、無理に1位を選ばず、他の基準で決めてよいという情報として渡します。運用のしやすさ、既存の手順との近さ、担当者の慣れといった、費用以外の基準です。

なお、厳密には差の判定は、それぞれの区間を眺めるのではなく、差そのものの信頼区間で行うのが本筋です。第9章と第11章で扱ったとおり、共通乱数法を使っていれば、方策ごとの費用が同じ方向にぶれるため、差の区間は個々の区間から想像するより狭くなります。したがって今回の比較は、実際にはより厳しく判定できる余地があります。それでも1位と2位の差は明確、3位と4位の差は微妙という関係は変わりません。

もう1つ、出力から読める事実があります。半幅が点によって0.14から0.33まで幅を持っていることです。同じ60回の反復をかけているのに、方策によってばらつきの大きさが違います。これは、全ての候補に同じ計算予算を配るのが必ずしも効率的ではないことを示しています。ばらつきの小さい点は少ない反復で十分に判定でき、ばらつきの大きい点にはもっと反復が要ります。この観察が、次節で扱うランキングと選択の考え方につながります。

上位6件の並びからも構造が読み取れます。6件のうち4件が発注点60であり、残る2件も発注点40です。発注点を60より高くした組み合わせは上位に1つも入っていません。この設定では、届くまでの3日間に見込まれる需要が平均で60個ですから、発注点60はちょうどその見込みを賄う水準にあたります。目標在庫のほうは80から140まで幅広く上位に現れており、費用の水準は目標在庫より発注点で大きく決まっていることが分かります。どのパラメータが効いているのかという情報は、最良の1点よりも、現場での運用設計に役立つことがあります。

比較費用の差半幅との関係報告のしかた
1位と2位2.42半幅0.16と0.14に対して十分大きい差は明確。1位を採る根拠になる
3位と4位0.47半幅0.31と0.25と同程度区別がつかない。費用以外の基準で決める
1位と5位8.32半幅をはるかに超える差は明確。5位は候補から外してよい

探索の手法を並べる、格子探索から確率的勾配法まで

探索の手続きには系統がいくつもあります。どれが良いかは、パラメータの数、候補が連続か離散か、評価1回にかかる時間で決まります。順に見ていきます。

まず全数探索と格子探索です。候補を全部評価するか、連続パラメータを一定の刻みで区切って格子状に評価します。今回のコードがこれにあたります。パラメータが2つか3つで、範囲が狭く、評価が安いときには、これで十分です。利点は結果が解釈しやすいことです。全ての点の成績が手元に残るので、どのパラメータが効いているか、最良点の周りがなだらかか急峻かといった情報がそのまま読めます。欠点はパラメータが増えると破綻することで、刻み数の累乗で候補が増えます。

次にランキングと選択です。候補が有限個で、そのどれが最良かを見分けたいときの考え方です。全ての候補に同じ回数の反復を配るのをやめ、有望な候補に計算予算を厚く配ります。序盤に全候補を少しずつまわして見当をつけ、明らかに劣る候補は早めに打ち切り、残った候補に反復を追加していきます。狙いは、総計算量を抑えたまま「最良を選び損ねる確率」を一定以下に保つことです。今回の実験のように、全候補に一律60回を配ると、上位争いに関係のない点にも同じ計算がかかります。その分を上位の判定に回せば、同じ計算量でより確かな結論が出せます。

3つ目がメタヒューリスティクスです。遺伝的アルゴリズム、焼きなまし法、タブー探索といった系統がここに入ります。共通する発想は、良い解の近くを重点的に探しつつ、ときどき遠くへ飛んで局所解から抜け出すというものです。目的関数の性質をほとんど仮定しないので、適用範囲が広く、離散と連続が混ざった問題にも使えます。商用のシミュレータに付属する最適化機能は、この系統を採用しているものが多くあります。弱点は評価回数を多く必要とすることと、最適性の保証が得られないことです。

4つ目がメタモデルを使う系統で、これは次の節でまとめて扱います。

5つ目が確率的な勾配法です。勾配が取れないので、パラメータを少しずらして評価し、その差から勾配を近似します。素朴にやると、パラメータの数だけ差分の評価が要るため高価になります。同時摂動確率近似は、全パラメータを同時にランダムな向きへずらして2回だけ評価し、そこから勾配の方向を推定する手法で、パラメータが多いときに評価回数を大幅に減らせます。ノイズを含む評価に対して、繰り返しの中で徐々に真の方向へ収束していくという設計になっています。パラメータが連続で、数が多く、目的関数が滑らかだと期待できる場合に向きます。

手法向く場面必要な評価回数注意点
全数探索・格子探索パラメータ2つか3つ、範囲が狭い、評価が安い刻み数の累乗パラメータが増えると破綻する。刻みの粗さが結論を左右する
ランキングと選択候補が有限個で、最良を確実に見分けたい一律配分より少なくて済む候補が多すぎると使えない。打ち切り基準の設計が要る
メタヒューリスティクス離散と連続が混在、構造が読めない多い最適性の保証がない。ノイズへの対処が別途必要
メタモデル評価が非常に高価、パラメータが中程度の数少ない代理モデルが外れると誤った方向へ進む
確率的勾配法連続パラメータが多く、滑らかさが期待できる中程度刻み幅と収束の設定が難しい。局所解に入る

手法を選ぶときの目安は、評価1回のコストとパラメータの数の2軸で考えると整理しやすくなります。評価が安くパラメータが少なければ格子探索、評価が安くパラメータが多ければメタヒューリスティクス、評価が高価であればメタモデル、候補が有限で最良の判定そのものが目的ならランキングと選択、というのが大まかな対応です。

メタモデル、軽い代理を作って当たりをつける

メタモデルとは、シミュレーションの入出力の関係を、軽い数式モデルで近似したものです。代理モデルや応答曲面と呼ばれることもあります。手順は3段階です。まず、いくつかの点でシミュレーションをまわして入出力の組を集めます。次に、その組から入力と出力の関係を表す軽いモデルを当てはめます。最後に、そのモデルの上で最適化を行い、有望だと予測された点だけを本物のシミュレーションで確かめます。

この手順が効くのは、軽いモデルの評価がほぼ無料だからです。シミュレーション1回に3分かかるとしても、当てはめた二次式や回帰モデルの評価は一瞬で終わります。そこで、探索の大部分を代理の上で済ませ、本物のシミュレーションは確認のためだけに使います。評価が高価な問題に対する定石と言ってよい発想です。

代理モデルの作り方には段階があります。最も素朴なのが応答曲面法で、対象とする範囲を狭く取り、その中で入力と出力の関係を一次式または二次式で近似します。二次式であれば頂点を解析的に求められるので、そこが最良点の候補になります。範囲を移しながらこれを繰り返し、少しずつ良い領域へ移動していくという使い方をします。第11章で扱った実験計画法は、この当てはめのために、どの点を評価すべきかを決める部分にあたります。

クリギングは、点と点の距離が近いほど出力も近いという仮定を置いて、評価していない点の値を確率的に予測する手法です。予測値だけでなく、予測の不確かさも一緒に出せることが特徴です。地質学の分野で発展した手法で、シミュレーションの近似にも広く使われています。

ベイズ最適化は、この不確かさの情報を探索に使う考え方です。次にどこを評価するかを決めるときに、予測値が良い点だけを選ぶのではなく、予測が不確かな点も選びます。予測が良い点を選ぶのは、すでに分かっている良い領域を掘り下げる動きで、予測が不確かな点を選ぶのは、まだ見ていない領域を確かめる動きです。この2つの釣り合いを取りながら候補を出していきます。評価回数が数十回から数百回に限られる場面で、よく使われます。

メタモデルの注意点は、代理が外れたときに探索全体が誤った方向へ進むことです。代理モデルは、当てはめに使った点の周りでは信頼できますが、離れた領域では外挿になります。外挿の領域で「ここが最良だ」と予測されたら、その点は必ず本物のシミュレーションで確かめる必要があります。確かめずに代理の予測をそのまま結論にするのが、この系統で最も起こりやすい失敗です。代理はあくまで当たりをつけるための道具で、最終的な判断は本物の評価で行います。

最適化とシミュレーションを併用する3つの型

ここまでは、シミュレーションを評価関数にして探索をまわす形を扱ってきました。実務での併用はこれだけではありません。順序と役割の組み合わせで、大きく3つの型に分かれます。

型①は、数理最適化で解を作り、シミュレーションで検証する形です。生産計画、配送ルート、要員のシフトといった計画は、数理最適化で作るのが一般的です。ただし、その計画は決まった処理時間、決まった移動時間、故障なしといった前提で組まれています。実際には処理時間はばらつき、渋滞は起き、機械は止まります。そこで、できた計画をシミュレーションに入力して、ばらつきのある世界で実行可能かを確かめます。納期の遵守率、遅延の分布、余裕のなさが集中している工程を見て、必要なら制約を足して最適化をやり直します。

型②は、シミュレーションを評価関数にして探索する形です。本章の主題であり、コードで示したものがこれにあたります。目的関数を式に書けない場合、書けても現実を写しきれない場合に選びます。倉庫の作業ルール、コールセンターの配席、生産ラインの緩衝在庫のように、成績が動的な相互作用で決まるものが対象になります。

型③は、粗い最適化で候補を絞り、シミュレーションで精査する二段構えです。候補が膨大にあるとき、まず単純化した数理モデルで解いて、上位の数個から数十個に絞ります。そのうえで、絞った候補だけを詳細なシミュレーションで比較します。単純化したモデルは現実を写しきれていないので、そこで出た1位が本当の1位とは限りません。だからこそ、1位だけを残すのではなく、上位の複数を残してシミュレーションに渡すことが重要になります。

流れ向く場面注意点
①検証型最適化で計画を作り、シミュレーションで実行可能性を確かめる計画は式に書ける。実行段階のばらつきが心配検証で落ちたとき、制約をどう足すかの設計が要る
②評価関数型シミュレーションを目的関数として、その外側で探索する目的関数を式に書けない。動的な相互作用が成績を決める評価が高価でノイズがある。予算の設計と差の判定が要る
③二段構え型粗い最適化で候補を絞り、シミュレーションで精査する候補が膨大で、全部をまわす余裕がない絞り込みで真の最良を落とす危険。上位を複数残す

3つの型に共通するのは、最適化とシミュレーションが同じ問題の別々の側面を担当しているという構造です。最適化は候補の空間を効率よく動く役割、シミュレーションは候補の良し悪しを現実に近い条件で測る役割。どちらか一方だけでは、扱える範囲か、答えの確からしさのどちらかが欠けます。

最適化とシミュレーションの併用3類型を表すフロー図

実務上の注意、評価予算と選抜の楽観

最後に、シミュレーション最適化を実務で回すときに必ず確認したい点を挙げます。手法の選択よりも、こちらのほうが結果を左右することが多いように思います。

第1に、評価1回のコストを先に測ります。探索の設計は、何回評価できるかという予算から逆算して決まります。1回30秒のモデルで1000回評価すれば8時間以上かかります。1回3分なら50時間を超えます。この見積もりを最初にやらないまま探索を組むと、走らせてから間に合わないことに気づくことになります。測るのは簡単で、代表的な設定を1回まわして時間を計り、必要な反復回数を掛ければ済みます。

第2に、並列化が効きます。シミュレーション最適化の計算は、独立な単位に分かれています。同じ方策の反復どうしは独立ですし、格子探索やメタヒューリスティクスの世代内の候補どうしも独立です。したがって、複数のプロセスやクラウドの複数インスタンスに分けて同時に流せます。逐次で8時間かかる計算が、16並列なら30分程度になります。乱数の種を重複させないことだけ注意すれば、実装も難しくありません。手法の工夫より並列化のほうが効く場面は珍しくありません。

第3に、最良解の性能は楽観に偏ります。これは見落とされやすい性質です。多数の候補をノイズのある評価で比べて最良を選ぶと、選ばれた候補には「本当に良い」効果と「たまたま運が良かった」効果の両方が乗ります。54通りの中から最も低い値を選べば、その値は真の性能より低めに出ている可能性が高くなります。選抜という操作自体が、選ばれたものの推定値を良い方向へ歪めるためです。

対処は簡単です。選んだあとに、その解だけを独立な乱数で再評価します。探索に使った乱数の種とは別の種で、十分な反復をかけてまわし直し、その値を報告値とします。この一手間を入れないまま探索中の最良値を報告すると、実運用に移したときに「思ったほど効果が出ない」という結果になります。手順として必ず組み込むべきものです。

第4に、過剰に細かく最適化しません。入力の分布は推定値であり、費用の単価にも幅があり、需要の見込みには来年の不確かさがあります。入力の精度が数%の水準なのに、出力の0.1%の差を追って解を選んでも、その差は入力の誤差に飲み込まれます。今回の例で言えば、発注点を60にするか65にするかを詰める前に、需要の平均が本当に20なのか、届くまでが本当に3日なのかを確かめるほうが、費用への効き方は大きくなります。感度分析で、入力を動かしても最良点が変わらない範囲を確認しておくと、どこまで詰める価値があるかの見当がつきます。

注意点やること怠るとどうなるか
評価コストの見積もり代表設定を1回まわして時間を計り、予算を逆算する走らせてから期限に間に合わないと気づく
並列化反復と候補を独立な単位に分けて同時に流す手法の工夫で取り返せない時間差が生まれる
選抜バイアスの除去選んだ解だけを独立な乱数で再評価して報告する実運用で効果が出ず、モデルの信頼を失う
過剰最適化の回避入力の精度を確認し、感度分析で詰める範囲を決める誤差に埋もれる差を根拠に案を選んでしまう

本章の締めくくりとして、第1章の整理に戻ります。最適化とシミュレーションは対立する技術ではありません。答える問いが違うだけです。決めたい問いには最適化が向き、確かめたい問いにはシミュレーションが向きます。そして、決めたいけれど式に書けないという問いに対しては、両者を組み合わせます。

組み合わせるときに手放してはいけないのは、シミュレーションの側が持ち込む性質のほうです。値はぶれる、評価は高価、勾配はない。この3つを忘れて、最適化の作法のまま順位表を作ると、区別がついていない差を根拠に案を選ぶことになります。差の大きさで語り、選んだあとに独立な乱数で確かめる。この2つを手続きとして組み込めるかどうかが、シミュレーション最適化を意思決定に使えるものにするかどうかの分かれ目になります。

この章を深めたい方への参考書籍

OR入門(宮川公男、丸善出版)
オペレーションズ・リサーチの全体像を1冊で見渡せる入門書です。シミュレーションが数理最適化や待ち行列とどう並ぶ道具なのかを、分野の地図として確かめたいときの出発点になります。

第13章 業務課題別の使いどころ、どの部門の何に効くか

ここまでの章では、モデルの型と、それを支える理屈と、結果を信じるための手続きを順に扱ってきました。道具の説明としては一通り揃っています。しかし実務でこの技術に接する方の多くは、道具の側からではなく、困りごとの側から入ってきます。工場のラインが詰まる、倉庫の出荷が夕方に集中する、コールセンターの放棄呼が減らない、といった具体的な症状が先にあって、それに使える道具があるかどうかを知りたい、という順序です。

この章は、その順序に合わせて書いています。領域ごとに、実際によく持ち込まれる問いを並べ、それにどの型が向いているか、モデルから何が出てくるか、どこで失敗しやすいかを整理します。読み方としては、自分の部門に近い節を先に読み、次に章の後半にある横断的な表を見て、自社の状況が「検討してよい兆候」に当てはまるかを確かめる、という使い方も想定しています。

あらかじめ断っておくと、領域が違っても問いの構造は驚くほど似ています。資源が有限で、そこに仕事が不規則に到着し、順番待ちが起きる。この形が、工場でも倉庫でも病院でもコールセンターでも繰り返し現れます。だからこそ、離散イベントシミュレーションという1つの型が、これほど広い領域で使われています。領域ごとの節を読み進めると、名前が違うだけで同じ構造だと分かる箇所が何度も出てきます。その気づきは、自分の部門にない事例からも学べるという意味で、実務上そのまま役に立ちます。

領域が違っても、問いの形は繰り返される

個別の領域に入る前に、問いの形を4つに分けておきます。この分類は、どの型を選ぶかの判断とほぼ一対一で対応します。

1つ目は、能力の問いです。「この体制で、1日にどれだけ処理できるのか」「どこが詰まっているのか」「あと何台増やせば足りるのか」といった問いがここに入ります。有限の資源を仕事が奪い合う構造なので、離散イベントシミュレーションが基本になります。単純な構成であれば第5章の待ち行列の式で概算できますが、工程が連なっていたり、優先順位や段取りの制約があったりすると、式では追えなくなります。

2つ目は、不確実性の問いです。「最悪の場合どこまで悪化するのか」「この投資が回収できない確率はどれくらいか」「余裕をどれだけ持てばよいのか」といった問いです。ここではモンテカルロ法が中心になります。答えとして返ってくるのは1つの数値ではなく分布であり、そこから分位点を読んで意思決定に渡します。

3つ目は、構造の問いです。「なぜこの数字は改善したあとに悪化するのか」「対策を打ったのに効果が半年遅れて消えるのはなぜか」といった、時間の遅れとフィードバックが絡む問いです。個々の待ち行列ではなく、量の増減とその因果の輪が主役になるので、システムダイナミクスが向きます。

4つ目は、個の振る舞いから全体が決まる問いです。「値上げしたら客はどう動くのか」「混雑を避ける行動が、かえって別の場所を混雑させないか」といった、主体ごとに判断が違い、その相互作用が結果を左右する問いです。エージェントベースモデリングの領分になります。

実務の課題は、この4つのどれか1つに綺麗に収まるとは限りません。むしろ複数にまたがるのが普通で、その場合は主となる型を1つ決めたうえで、他の型を部分的に組み合わせます。よくある組み合わせは、離散イベントで工程を表現しつつ、入力となる需要や故障をモンテカルロで振る、という形です。以下の各節では、その主従の関係も併せて書いています。

製造、ラインの能力とボトルネックを読む

製造現場から持ち込まれる問いは、能力の問いに集中します。代表的なものを並べると、このラインの実力値はいくつか、どの設備が全体の足を引っぱっているのか、段取り替えの回数を減らすとロットが大きくなって仕掛が増えるがどちらが得か、新しい設備を入れると全体の処理量はどれだけ増えるのか、工程間の仕掛置き場をどこにどれだけ持つべきか、多品種を流すときの投入順序をどう決めるか、といったところです。故障と保全の計画、つまり予防保全の周期をどう置くかも、この枠に入ります。

主となる型は離散イベントシミュレーションです。製造ラインは、工程という資源に、製品というエンティティが並んで順番を待つ構造そのものです。加工時間のばらつき、段取り替えに要する時間、設備の故障と復旧、作業者の割り付け、といった要素をそのまま表現できます。需要の変動や故障の発生をどう振るかという部分に、モンテカルロ法の考え方が入ります。

出力として得られるのは、単位時間あたりの生産数、製品が投入されてから完成するまでのリードタイムの分布、設備ごとの稼働率と、稼働していない時間の内訳、工程間の仕掛品の推移です。ここで重要なのは、リードタイムが平均値ではなく分布で出てくる点です。納期遵守率を議論するときに必要なのは平均ではなく、上側の分位点だからです。平均リードタイムが3日でも、10回に1回7日かかるなら、納期の約束は7日側で立てる必要があります。

注意点は、入力データの整備に尽きます。実作業時間、段取り時間、故障間隔と復旧時間、これらの実測値がないままモデルを作ると、出てくる数字は前提の写しでしかありません。設計上の標準時間だけを入れたモデルは、現場が日々やりくりしている実態を表しません。第8章で扱ったとおり、平均値だけでなくばらつきの形まで押さえる必要があります。データが足りないと分かった段階で、まず計測から始めるという判断も、プロジェクトとしては正しい選択です。

もう1つ、製造でよく起きるのは、ボトルネックが固定されていないという事実の受け入れです。品種構成が変われば、律速になる工程も移動します。ある品種を流すときは組立が詰まり、別の品種では検査が詰まる、という状況では、単一のボトルネックを前提にした改善は空振りします。シミュレーションの価値は、この移動を計算のうえで見せられることにあります。

物流と倉庫、波動とマテハンの台数を決める

倉庫と物流拠点の問いは、波動という言葉に集約されます。入荷は午前に集中し、出荷は締め時間の前に集中する。曜日によっても季節によっても山の高さが変わる。この不均一な負荷に対して、人員と設備をどれだけ用意し、どう配置するかが問われます。具体的な問いとしては、ピッキング方式を摘み取りから種まきに変えると出荷能力はどう変わるか、自動倉庫や搬送機を何台入れれば締め時間に間に合うか、トラックを着けるバース(荷役をする車両の停車場所)の数が足りているか、庫内のレイアウトを変えると歩行距離はどれだけ減るか、配送先をどう束ねると車両数が減るか、といったものが挙がります。

中心となる型は離散イベントシミュレーションです。オーダーがエンティティ、作業者と機器と通路とバースが資源になります。需要の不確実性、つまり日々のオーダー件数や行数の揺れは、モンテカルロで振ります。配送ルートの束ね方については、経路そのものの決定は数理最適化の領分であり、シミュレーションは決めた方策が変動のもとでどう振る舞うかを確かめる側にまわります。第12章で扱った、最適化と評価の組み合わせが自然に現れる領域です。

出力は、時間帯ごとの処理件数、締め時間までに出荷が完了する確率、作業者と機器の稼働率、待ちが発生している箇所と待ち時間、バースの待機台数と待機時間、通路の混雑度合いです。締め時間に間に合う確率という形で出せるのが、この領域の実務的な強みです。平均の処理能力が十分でも、山の時間帯に間に合わなければ意味がないので、判断の材料は確率でなければなりません。

注意点は2つあります。第一に、人の歩行や作業の細部をどこまで表現するかの線引きです。棚の位置まで再現したモデルは精密ですが、作るのも維持するのも重くなります。ピッキング方式の比較が目的なら、歩行距離を統計的な分布で与えれば十分なことが多くあります。第二に、波動の形そのものが入力の要です。1日の平均件数だけでモデルを作ると、山の時間帯の詰まりが消えてしまい、能力が過大に見積もられます。

小売と店舗、待ち時間と動線を同時に見る

店舗の問いは、客が実際に体験する待ち時間と、店内での動きに関わります。レジを何台開ければ待ち時間が許容範囲に収まるか、セルフレジの比率をどこまで上げるべきか、有人レジとセルフレジを客がどう選ぶか、通路や特定の売り場に人が滞留していないか、欠品が起きたときに客はどう振る舞うか、キャンペーンで来店が増えたときに何が最初に破綻するか、といった問いが並びます。

この領域は、離散イベントシミュレーションとエージェントベースモデリングの組み合わせが向きます。レジの待ち行列は前者で素直に表現できますが、客がどのレジを選ぶか、混んでいるのを見て諦めるか、店内をどう回遊するかは、個々の主体の判断です。第6章で扱ったように、選択の規則を主体ごとに持たせて動かすと、平均だけでは説明できない偏りが再現できます。全レジの平均待ち時間は短いのに、特定のレジにだけ列が伸びる、といった現象がその典型です。

出力は、時間帯別の待ち時間の分布、待ち時間が一定を超える客の割合、レジごとの稼働率と偏り、店内の滞留箇所と滞留時間、セルフレジでの支援要員の呼び出し回数などです。滞留の分布は、レイアウト変更の効果を議論するときの共通の土台になります。

注意点は、客の行動規則をどこまで根拠づけられるかです。行動規則は、モデルの中で結果を大きく左右する部分でありながら、実測が難しい部分でもあります。ここを想像で置くと、モデルは作った人の思い込みを増幅するだけの装置になります。入店から退店までの時間や、レジ選択の実績など、観測可能な指標でモデルの振る舞いを照合する作業が欠かせません。第10章の妥当性確認の考え方が、そのまま必要になります。

コールセンターとバックオフィス、呼量に対して人を置く

コールセンターは、この技術がもっとも早くから使われてきた領域の1つです。問いは明快で、時間帯ごとに何席用意すれば応答品質を守れるか、休憩と交代をどう組めば穴が開かないか、スキル別のルーティングを変えると全体の待ち時間はどうなるか、放棄呼をどこまで減らせるか、在宅勤務と出社を混ぜたときに何が変わるか、といったところです。バックオフィスの事務処理も、電話が書類に変わるだけで構造は同じです。

主となる型は離散イベントシミュレーションで、その土台には第5章で扱った待ち行列の理屈があります。呼量に対して必要な席数を求めるアーランC式は、この領域の設計で長く使われてきた道具です。式で足りるなら式で済ませるべきで、単一のスキル、単一の待ち行列、放棄なしという条件であれば、式の答えとシミュレーションの答えは一致します。式では扱えないのは、スキルベースのルーティング、途中で諦める客、折り返しの発生、席数が時間帯ごとに変わる状況、複数の窓口をまたぐ処理といった要素です。実務の設計はたいていこれらを含むので、式で当たりをつけてからシミュレーションで詰める、という順序が現実的です。

出力は、時間帯別の応答率と平均応答速度、待ち時間の分布、放棄呼の率、席ごとの占有率、スキル別の負荷の偏り、シフト案ごとの必要人数です。応答率のような目標指標を、シフト案ごとに確率で比較できるところに価値があります。

注意点は、呼量の予測とシミュレーションの役割を混同しないことです。来週の呼量が何件になるかは予測の問題であり、この技術が答えるものではありません。シミュレーションが答えるのは、予測された呼量とその不確実性のもとで、この配置がどれだけ耐えるか、です。予測の幅をそのままモデルに入れ、上振れした場合の応答率まで見ておくと、シフトの余裕をどこに置くかの議論がしやすくなります。

医療、待ち時間と資源の割り付けを検討する

医療の現場は、資源の制約が厳しく、需要の変動が大きく、しかも人命に関わるという条件が揃っています。持ち込まれる問いは、外来の待ち時間を減らすには予約枠をどう区切ればよいか、検査機器の枠をどう配分すれば装置の稼働と患者の待ちが両立するか、病床の運用をどう変えれば受け入れを断らずに済むか、救急の受け入れ体制が需要のピークに耐えられるか、手術室のスケジュールをどう組めば終業時刻の超過が減るか、といったものです。

型は離散イベントシミュレーションが中心です。患者がエンティティ、診察室と検査機器と病床と手術室と職員が資源になります。緊急度による優先順位、途中で割り込む急患、複数の資源を同時に押さえないと始められない処置といった構造は、この型でそのまま表現できます。需要の変動や在院日数のばらつきは分布として与えます。

出力は、待ち時間の分布、資源ごとの稼働率、待ち行列の長さの推移、受け入れを断る確率、終業時刻を超過する確率、患者が院内で過ごす総時間などです。予約枠の設計では、枠を詰めると装置の稼働は上がるが待ちが急に伸びる、という関係が数字で見えます。第5章で扱った利用率と待ち時間の非線形な関係が、もっとも実感しやすい形で現れる領域でもあります。

注意点として、妥当性確認の要求水準が他の領域より高くなることを挙げておきます。判断の結果が患者の安全に直結するため、モデルの前提が現場の運用と食い違っていないことを、担当する医療者と一つずつ確認する必要があります。第10章で扱った手続きのうち、現場の熟練者に構造と結果を見せて違和感を聞く手順は、この領域では省略できません。また、扱うデータに個人の情報が含まれるため、匿名化と取り扱いの管理が前提条件になります。加えて、モデルが答えられる範囲を明示しておくことも重要です。運用の効率を扱うモデルは、臨床上の判断について何も述べません。その境界を文書に書いておかないと、範囲外の議論に持ち出される危険があります。

金融とリスク、分布そのものを成果物にする

金融とリスク管理の領域では、問いの形が他と少し違います。待ち行列ではなく、不確実性の問いが中心になります。この事業の損失はどこまで膨らみうるのか、与信ポートフォリオ全体で同時に焦げつく確率はどれくらいか、資産の組み合わせを変えると数年後の資産額の分布はどう変わるのか、保険の支払いが想定を超える確率はどれくらいか、経済環境が急激に悪化したときに自己資本は耐えるのか、といった問いです。

主役はモンテカルロ法です。第3章で扱ったとおり、入力の不確実性を分布として与え、何万回も試行して結果の分布を作り、そこから分位点や超過確率を読みます。損失の裾がどこまで伸びるかを知りたい場合、平均は役に立ちません。上位1パーセントの水準で何が起きるかが知りたいので、結果の分布そのものが成果物になります。時間の経過に沿って状態が遷移する構造を持つ場合、たとえば与信先の格付が年ごとに変わっていく過程などは、離散的な状態遷移を伴うモデルとして組み立てます。

出力は、損失や資産額の分布、指定した分位点の値、目標を下回る確率、悪条件のシナリオごとの結果、要素ごとの寄与度です。寄与度は、どの要因が結果のばらつきを生んでいるかを示すもので、対策の優先順位づけに使えます。

注意点として最も大きいのは、相関の扱いです。第3章で述べたとおり、個々の変数の分布が正しくても、変数どうしの連動の仕方を誤ると、結果の裾はまったく違う形になります。個別には低い確率の事象が、同時に起きる確率を過小に見積もると、全体のリスクを大きく取り違えます。とりわけ、平常時には相関が低く、悪条件のもとでのみ相関が高まるという性質は、独立を仮定したモデルでは決して現れません。相関を独立と置いた計算は、扱いやすい代わりに、危機の局面でこそ外れます。相関の設定は、モデルの中で最も慎重に扱うべき箇所であり、感度分析で結果がどこまで動くかを確認しておく必要があります。

人員計画と組織、遅れのある系として見る

採用と育成の計画は、待ち行列よりも、量の増減とその遅れの問題です。よく持ち込まれる問いは、今の採用ペースで3年後の要員は足りるのか、育成に要する期間を考えるといつから採用を増やすべきか、離職率が1ポイント上がると必要な採用数はどれだけ増えるのか、熟練者の退職が集中する時期に技術の継承が間に合うのか、特定のスキルを持つ人材の構成比が将来どうなるのか、といったものです。

ここで向いているのはシステムダイナミクスです。第7章で扱ったストックとフローの考え方が、そのまま当てはまります。要員をスキル段階ごとのストックとして置き、採用・育成による昇格・離職・退職をフローとして表します。すると、採用してから戦力になるまでの遅れ、忙しくなると育成に手が回らず、育成が遅れるとさらに忙しくなるというフィードバックの輪が、構造として現れます。個別の人の異動を追うのではなく、量の流れとその遅れを見るのが、この型の得意分野です。

出力は、スキル段階ごとの要員数の推移、必要人数に対する過不足の推移、採用方針を変えた場合の将来の姿の違い、逼迫が起きる時期とその深さ、離職率などの前提を動かしたときの感度です。要員が不足する時期が事前に見えると、採用の意思決定を前倒しする根拠になります。

注意点は、モデルの粒度です。個人単位で追いたくなりますが、それをやるとデータの整備と維持が重くなるうえ、人事上の機微な情報を扱うことになります。方針の比較が目的であれば、段階ごとの人数という粒度で十分に議論できます。もう1つは、育成期間や離職率といった前提の置き方です。これらは実績から取るべきで、希望的な数字を置くと、結論は都合よく歪みます。前提を1つずつ動かして結果の変化を見せる形にしておくと、議論が前提の妥当性に向かうので健全です。

サプライチェーン全体、増幅と方策を扱う

個々の拠点ではなく、調達から販売までの連なり全体を対象にすると、問いの性質がまた変わります。末端の需要は安定しているのに上流の生産計画が振れるのはなぜか、在庫の持ち方を変えると欠品と在庫費用のどちらがどれだけ動くか、リードタイムを短縮すると必要な在庫はどれだけ減るか、供給元が止まったときに何週間持ちこたえられるか、拠点を集約すると全体の在庫は減るのか、といった問いです。

この領域では3つの型を併用します。需給の増幅や在庫の振動といった構造の癖は、システムダイナミクスで見るのが素直です。第7章で扱ったブルウィップ効果、つまり末端の需要のわずかな変動が、上流にさかのぼるほど大きく増幅されて伝わる現象がその代表です。需要や供給の不確実性、供給途絶の発生はモンテカルロで振ります。個々の拠点や工程の中身、たとえば庫内作業や生産工程の詰まりまで見たい場合は、その部分を離散イベントで表現します。どこを粗く、どこを細かく見るかを、答えたい問いから逆算して決めることになります。

出力は、各段階の在庫と発注量の推移、欠品率と充足率、在庫を持つ費用、需要変動が上流に伝わるときの増幅の度合い、供給途絶が起きた場合の影響の広がりと復旧までの期間です。方策を比較する場合、たとえば発注点と補充量を定める方式と、定期的に見直す方式とでは、平均在庫と欠品率の組み合わせがどう変わるかを、同じ需要系列のもとで比べられます。第11章で扱った共通乱数法が、この比較を鋭くします。

注意点は、増幅の原因の多くが、需要そのものではなく運用の規則にあるという点です。まとめ発注、価格変動に反応した先食い、欠品を恐れた水増し発注、こうした人と制度の振る舞いが増幅を生みます。したがってモデルには、物流の設備だけでなく、発注の意思決定規則を入れないと現象が再現できません。逆に言えば、この規則を変えたときの効果を試せるのが、この領域でシミュレーションを使う最大の理由です。設備投資をせずに運用規則を変えるだけで振れ幅が縮む、という結論が出ることも珍しくありません。

交通、人流、公共、多数の主体の相互作用を見る

都市や施設の混雑、避難、公共サービスの制度設計も、この技術がよく使われる領域です。問いとしては、信号や動線を変えると混雑はどれだけ緩和されるか、災害時にこの施設から全員が避難するのに何分かかるか、出入口を増やすとどこが新しい詰まりになるか、大規模なイベントの前後で交通がどこまで滞留するか、料金や制度を変えたときに人々の行動と全体の負荷はどう変わるか、といったものが挙がります。

型はエージェントベースモデリングと離散イベントシミュレーションの併用です。個々の人や車が、周囲の状況を見て進路や出発時刻を選ぶという構造は、主体ごとに規則を持たせる型でなければ表現できません。一方で、改札やゲートや窓口といった有限の処理能力を持つ地点は、待ち行列として扱うのが自然です。両者を組み合わせて、主体の移動と地点の処理を同時に動かします。

出力は、移動に要する時間の分布、混雑が発生する地点と時間帯、密度の推移、避難の完了までに要する時間、制度変更に対する利用者の行動の変化と、それが全体の負荷に与える影響です。避難計画のように、実物で試すことが危険であるか、そもそも試せない対象を扱えるのが、この領域での大きな利点です。

注意点は2つあります。第一に、主体の行動規則の妥当性です。人がどう判断するかという部分は、観測が難しく、状況によって変わります。避難行動のように、平常時のデータから外挿しにくい対象では、規則の置き方によって結果が大きく変わります。複数の規則で計算し、結果がどの範囲に収まるかを示すほうが誠実です。第二に、公共の意思決定に使う場合、結果が住民や利用者への説明に使われる可能性があります。前提と適用範囲を明示し、結果を1つの数値で断定しない形にしておく必要があります。

業務領域と4つのモデルの型の対応を表すマトリクス図

領域と型の対応を一枚に置く

ここまでの9領域を、1つの表にまとめます。実務でよくある問いと、主に使う型と、モデルから返ってくる主な指標を並べています。自社の課題に近い行を見て、その節に戻る使い方を想定しています。表の「主に使う型」は、あくまで出発点です。問いが複数にまたがる場合は、主となる型を決めたうえで他の型を部分的に組み合わせます。

領域よくある問い主に使う型主な出力指標
製造ラインの実力値はいくつか。どこが詰まるか。段取りとロットのどちらを優先するか。設備投資の効果はどれだけか離散イベント。需要と故障はモンテカルロ単位時間の生産数、リードタイムの分布、設備別の稼働率と停止内訳、仕掛品の推移
物流と倉庫締め時間に間に合うか。ピッキング方式を変えるとどうなるか。機器は何台要るか。バースは足りるか離散イベント。オーダー量はモンテカルロ時間帯別の処理件数、締め時間に間に合う確率、作業者と機器の稼働率、待機時間、通路の混雑
小売と店舗レジは何台開けるか。セルフレジの比率をどこまで上げるか。どこに人が滞留するか。繁忙時に何が先に破綻するか離散イベントとエージェントベース待ち時間の分布、一定時間を超える客の割合、レジ別の偏り、滞留の場所と時間
コールセンターとバックオフィス時間帯ごとに何席要るか。スキル別の振り分けをどう組むか。放棄呼をどこまで減らせるか離散イベント。単純な構成は待ち行列の式応答率、平均応答速度、待ち時間の分布、放棄率、席の占有率、必要人数
医療外来の待ちをどう減らすか。検査枠をどう配分するか。病床と手術室をどう運用するか離散イベント。優先順位つき待ち時間の分布、資源別の稼働率、受け入れを断る確率、終業超過の確率
金融とリスク損失はどこまで膨らむか。同時に焦げつく確率はどれくらいか。悪条件で自己資本は耐えるかモンテカルロ損失と資産額の分布、分位点、超過確率、シナリオ別の結果、要因別の寄与度
人員計画と組織3年後に要員は足りるか。いつから採用を増やすか。継承は間に合うかシステムダイナミクススキル段階別の要員推移、過不足の推移、逼迫の時期と深さ、前提に対する感度
サプライチェーン全体なぜ上流ほど振れるか。在庫方策をどう選ぶか。供給が止まると何週持つかシステムダイナミクスとモンテカルロと離散イベントの併用段階別の在庫と発注の推移、欠品率、在庫費用、増幅の度合い、途絶時の影響と復旧期間
交通、人流、公共混雑をどう緩和するか。避難に何分かかるか。制度変更で行動はどう変わるかエージェントベースと離散イベント移動時間の分布、混雑地点と時間帯、密度の推移、避難完了までの時間、行動変化の影響

表を横に見ると、離散イベントシミュレーションが半数以上の行に現れます。有限の資源に仕事が並ぶという構造が、それだけ広く存在するということです。逆に、システムダイナミクスが主役になるのは、時間の遅れとフィードバックが問題の中心にある2領域に限られています。エージェントベースは、主体ごとの判断の違いが結果を左右する領域に集中します。型の選択は、業種ではなく問いの構造で決まる、という第2章の主張が、この表の形にそのまま出ています。

検討してよい兆候と、要らない兆候

次の表は、自社の状況がこの技術に向いているかを見分けるためのものです。左の列に当てはまる項目が複数あれば、検討する価値があります。右の列に当てはまるなら、別の道具のほうが速く安く答えが出ます。使わないと判断できることも、同じくらい重要な成果です。

観点検討してよい兆候不要と判断してよい兆候
待ちの有無行列や順番待ちが日常的に発生している。待ち時間の苦情がある待ちが発生しない。処理能力が需要を大きく上回っている
資源の競合複数の仕事が同じ設備や人を取り合っている。優先順位で揉める仕事ごとに専用の資源があり、取り合いが起きない
能力の把握「どこまで受けられるか」に誰も答えられない。限界が経験則でしか語られない能力の上限が明確で、実績とも一致している
投資の前評価設備やシステムに投資する前に効果を知りたい。案が複数ある投資判断が済んでおり、実行するだけの段階にある
試すことの困難さ実物で試すと高価か、危険か、業務を止める必要がある選択肢が2つ程度で、実際に試すのが安く早い
ばらつきの影響平均では説明できない事象が起きる。悪い日だけが極端に悪い変動が小さく、平均で議論しても結論が変わらない
個の違い主体ごとに行動や条件が違い、その違いが全体の結果を左右する対象が均質で、平均的な1体として扱って差し支えない
時間の遅れ対策の効果が数か月後に出る。改善したのに後から悪化する原因と結果が即座に対応し、遅れがない
解の求め方組み合わせや相互作用が複雑で、式に落とせない待ち行列の式や在庫の公式で答えが出る。表計算で足りる
データの有無実績データがある、または計測する用意があるデータがなく、計測する見込みもない。前提がすべて推測になる

右の列について補足します。式で解けるなら式で解くべきです。単一窓口の待ち時間や、需要が安定した状況での安全在庫は、公式で計算できます。シミュレーションは公式より柔軟ですが、その柔軟さの代償として、作る手間と、結果が統計的な推定値になるという不便さを負います。公式が使える範囲でわざわざ模擬する理由はありません。

選択肢が2つで、実際に試すのが安いという場合も同様です。1週間だけ運用を変えて結果を見られるなら、そのほうが確実です。ただし、比較したい効果が小さく、日々の変動に埋もれてしまう場合は話が別で、そのときは同じ条件で繰り返し試せるモデルのほうが差を見分けられます。第11章で扱った共通乱数法が効くのは、まさにこの状況です。

最後の行のデータについては、線引きが難しいところがあります。データが完全に揃うまで着手しないという構えでは、いつまでも始まりません。一方で、すべての前提が推測であるモデルは、作った人の思い込みを数字の形で出力するだけの装置になります。実務上の目安としては、結果を左右する主要な入力のうち、少なくとも中心となるいくつかについて実測値があるか、短期間で計測できる見込みがあること、そして残りの前提については幅を持たせて感度を確認できること、この2つが満たされていれば着手して差し支えありません。

公表されている事例から読み取れること

ここまでは領域ごとの一般論を書いてきました。ここで、公開されている事例をいくつか挙げておきます。掲載しているのは、企業や研究機関、官公庁が自ら公表している一次情報で数値まで確認できたものに限りました。数値まで公表されている事例は、海外のツールベンダーが自社サイトで公開しているものが中心です。国内では、用途や取り組みの内容は公表されていても、成果の数値までは公表されていないことが多くなっています。

国内の事例から見ていきます。新エネルギー・産業技術総合開発機構と産業技術総合研究所は、トヨタ自動車の協力のもと、工場の部品供給やピッキングにおける人とロボットの協働をデジタルツイン化する取り組みを公表しています。作業者の全身の動作や身体への負荷、安全の状態をリアルタイムに分析するもので、生産性の向上が10%から15%、人の負担の軽減が約10%と報告されています。

国土交通省のProject PLATEAUでは、構造計画研究所と大成建設が西新宿地区の歩行者の回遊行動をエージェントベースでシミュレーションした事例が公開されています。平常時とイベント時を比較したもので、実データとの一致率は70%以上と報告されています。エージェントベースのモデルは行動ルールの置き方が結果を支配するため、実データとの一致率を明示していること自体が、第10章で述べた妥当性確認の姿勢を示しています。

交通分野では、中央大学の田口東教授による首都圏鉄道の混雑分析があります。約47,000本のダイヤを120万の頂点と260万のリンクからなるネットワークとして表現し、大規模イベント開催時の集中を推計したもので、特定の駅でピーク時に約6倍の集中が起こりうること、別の駅では朝ラッシュのピークが最大40%増となりうることが示されています。実物で試すことが不可能な対象を、計算機の中で先に試すという使い方の典型です。

金融では、日本銀行のワーキングペーパーが、不均一な与信ポートフォリオの信用リスクを計量する「分割モンテカルロ法」を提案しています。通常の方法と比べて15分の1の計算時間で、近似精度のよい信用VaRが得られたと報告されています。第11章で扱った分散減少の発想が、実務の計算量の問題に直接効いている例です。

海外の事例では、成果の数値が具体的に公表されているものが見つかります。次の表は、ツールの提供元が公開している事例から、数値が明示されているものを整理したものです。

主体対象公表されている成果
ZAHORANSKY AG(ドイツ)生産ラインとマテリアルフローの検討生産時間を最大70%短縮、スループット20%増。最大16台を連結したラインで、実機との乖離が5%未満
Electrolux(スウェーデン)新しい生産ラインの立ち上げ前の検証開発期間を20%から30%短縮、コストを15%から20%削減。想定外の問題を16件発見し、6か月以上の生産時間と50万ドルを超える損失を回避できたと報告
Skarnes, Inc.(米国)自動倉庫とコンベヤループスループットが1時間あたり70パレットから100パレットへ、43%増
Ghafari Associates(米国)倉庫の仕分けラインの検討必要なコンベヤ速度が計画値では足りず、2倍必要と判明。モデルの構築に要したのは1日半
Johns Hopkins Health System(米国)救急部門の患者フローと病床の運用在院時間16%減。救急部門の心理サービスの待ち時間が61.8分から16.2分へ
Memorial Health System(米国)救急部門と手術室の拡張計画在院時間16%減、治療を受けずに帰った患者が53%減、患者満足度が57パーセンタイルから99パーセンタイルへ

これらの事例で注目したいのは、削減率そのものよりも、成果の性質です。ZAHORANSKYの事例で公表されている「実機との乖離が5%未満」は、改善効果ではなく、モデルがどれだけ現実を写せていたかを示す数字です。第10章で述べたとおり、シミュレーションが現場で使われるかどうかは精度そのものより信頼で決まりますが、その信頼を作るのはこうした突き合わせの記録です。

Electroluxの事例で公表されている「想定外の問題を16件発見した」も同じ種類の価値です。シミュレーションの成果は、しばしば「何%改善した」ではなく「作る前に問題が見つかった」という形で現れます。投資の前に問題を見つけたことの価値は、事後には測りにくく、報告書にも残りにくいのですが、実務上はこちらのほうが大きいことがあります。導入の効果を測る指標を設計するときは、この点を意識しておく必要があります。

もうひとつ、Ghafariの事例にある「モデルの構築に要したのは1日半」という記述も見落とせません。シミュレーションは大がかりな取り組みだと思われがちですが、問いが1つに絞られていれば、モデルは小さくて済みます。第16章で述べる「問いを1つに絞る」という進め方は、期間と費用に直接効いてきます。

どの領域でも、入口は同じ形になる

ここまで9つの領域を見てきましたが、導入の入口はどの領域でも同じ形をしています。困っている場所を1つ選び、その1つの問いに答えるモデルから始めることです。

この当たり前に見える原則が、実際にはよく破られます。せっかく作るのだから全社の物流を丸ごと表現しよう、将来どんな問いが来ても答えられるように汎用的に作ろう、という方向に話が広がりやすいためです。範囲を広げると、必要なデータの種類が増え、関係する部署が増え、合意しなければならない前提が増えます。その結果、最初の結果が出るまでの期間が延び、その間に組織の関心が離れ、途中で止まります。範囲の広いモデルは、精密である前に、完成しないという問題を抱えます。

1つの問いに絞ると、何が要らないかが決まります。倉庫の締め時間に間に合うかを問うなら、入荷側の細部は粗く扱ってよく、配送の経路は範囲外に置けます。問いが決まらないまま作り始めると、この判断ができないので、すべてを表現しようとして重くなります。範囲を決めるとは、何を捨てるかを決めることであり、それができるのは問いが1つに絞られているときだけです。

もう1つ、1つの問いから始めることには実務上の効用があります。最初のモデルが答えを出すと、関係者の側に「この道具は何に使えるか」の感覚が生まれます。すると次の問いが、こちらから提案するのではなく、現場の側から出てくるようになります。倉庫の締め時間を扱ったモデルが動けば、では入荷側はどうか、繁忙期はどうか、という問いが自然に続きます。同じ骨組みを持ったモデルなので、2つ目からは短い期間で作れます。範囲を広げるのは、この段階に来てからで遅くありません。

どの問いを最初に選ぶかについては、2つの条件を満たすものを探すのが実務的です。1つは、答えが出たときに何かが変わること。誰かが判断に使う予定のない問いは、どれだけ精密に答えても報われません。もう1つは、データが手元にあるか、短期間で計測できること。この2つを満たす問いは、たいてい現場がすでに困っている場所にあります。

選んだ問いをどのような順序でプロジェクトに組み立て、どこで躓きやすいか、そして関係者をどう巻き込むかについては、第16章で扱います。この章で自社に近い領域を見つけた方は、その問いを1つ持って第16章に進むと、進め方の話が具体的に読めるはずの内容になっています。

導入の入口を表すフロー図

この章を深めたい方への参考書籍

マルチエージェントによる金融市場のシミュレーション(高安美佐子・和泉潔・山田健太・水田孝信、コロナ社)
金融市場をエージェントベースで扱う研究をまとめた本です。個々の参加者の行動から市場全体の挙動が生まれる過程を、具体例として追えます。

待ち行列理論の基礎と応用(川島幸之助 監修、塩田茂雄・河西憲一・豊泉洋・会田雅樹 著、共立出版)
待ち行列理論の基礎から、コールセンタ設計や通信網の性能評価といった応用までを扱います。本章で式の形だけ示した部分を、導出から追いたい方に向きます。

第14章 ツールの地図とPythonでの実装

ここまでの13章で、シミュレーションが何をする技術か、どの型を選ぶか、入力をどう決め、出力をどう読み、どう検証するかを見てきました。まだ触れていないのが道具の話です。実際にモデルを組むには、何かしらの環境を選ばなければなりません。専用のシミュレータを買うのか、Pythonで書くのか、表計算で済ませるのか。この選択は、導入の初期にほぼ必ず議題になります。

この章では、まず道具を大づかみに分類し、選定のときに見るべき判断軸を整理します。後半では、Pythonの離散イベントライブラリである SimPy を題材に、コードの読み方を1つずつ説明します。第4章で使った窓口配置のコードを、今度は結果ではなく書き方の側から読み直す形になります。

ただし、この章の位置づけを先に断っておきます。道具の選択は、シミュレーションプロジェクトの成否を決める要因のなかでは、それほど上位に来ません。上位に来るのは、問いの立て方、入力データの質、検証の手続き、出力の読み方です。道具はそれらを支える器であって、器を良くしても中身が良くなるわけではありません。この章を「どれを買えばよいか」の答えとしてではなく、「何を基準に選ぶか」の整理として読んでいただければと思います。

道具は、問いが決まってから選ぶ

最初に置いておきたい原則は単純です。何を知りたいのかが決まる前に道具を選んではいけない、ということです。当たり前に聞こえますが、実務では逆の順序で進むことが少なくありません。「シミュレーションを導入しよう」という話が先に立ち、製品の比較表が作られ、デモを見て、予算が確保され、それから「では何をモデル化しますか」と問われる。この順序は、第16章で扱う失敗の型のなかでも、繰り返し観測される種類のものです。

順序が逆になると何が起きるか。まず、選んだ道具が得意な形に問いのほうが引き寄せられます。工場のライン向けの部品が充実したツールを買えば、ラインの検討をしたくなります。それが本当に最も効く課題であればよいのですが、たまたま道具があるからという理由で選ばれた課題は、投資に見合う成果を出しにくくなります。第13章で見たように、シミュレーションが効くのは「ばらつきと相互作用と資源の奪い合いがあり、実地で試すのが高くつく」という条件が揃った場所です。この条件は業務の性質で決まるのであって、道具の品揃えで決まるものではありません。

次に、費用の見積もりが狂います。シミュレーションプロジェクトの工数は、モデルを書く時間よりも、データを集めて整え、分布を決め、現場に確認し、検証する時間のほうが大きくなるのが普通です。第8章から第10章で扱った工程がそれにあたります。道具のライセンス費用と教育費用だけを積み上げた見積もりは、この部分をまるごと落としているため、実行段階で必ず超過します。

したがって、望ましい順序はこうなります。まず、答えたい問いを1つか2つに絞る。次に、その問いに答えるために必要なモデルの型を決める。第2章で扱った4つの型のうちどれか、あるいは組み合わせか。それから、そのモデルを作るのに必要な機能を列挙する。アニメーションが要るか、他システムとつなぐ必要があるか、誰が保守するか。最後に、その要件を満たす道具を候補から選ぶ。この順序であれば、道具は要件から逆算された結果として決まります。

もう1つ、実務的な提案があります。最初の1件は、手元にある無償の環境で小さく作ってみることです。Pythonでも表計算でも構いません。小さく作ると、データがどれだけ足りないか、現場のどの部分が思ったより複雑か、そもそも答えが出そうかが分かります。この段階で判明した要件をもとに道具を選べば、要らない機能に払う費用を避けられます。逆に、小さく作ってみて答えが出ないようなら、道具を買っても出ません。

道具を3つの層で捉える

シミュレーションの道具は、細かく見れば無数にありますが、実務的には3つの層に分けて考えると整理できます。汎用のプログラミング言語とライブラリの組み合わせ、専用のシミュレータ製品、そして表計算ソフトです。それぞれ、自由度と手軽さのどこに重心があるかが違います。

中身得意なこと自分で用意する必要があるもの
汎用プログラミング言語とライブラリPythonのSimPyやsalabim、エージェント向けのMesa、統計処理に強いRなど自由度が高い。他の分析やデータ処理、機械学習と地続きにつながる。テキストのコードなのでバージョン管理と自動実行に載る画面表示やアニメーション、実験の管理機能、モデル部品の在庫。プログラミングができる人
専用シミュレータ製品製造や物流に強い離散イベント系、システムダイナミクス系、複数の型を1つの環境で扱うマルチメソッド系モデル部品、アニメーション、実験の反復管理、最適化の探索機能が最初から揃っている。現場と絵を見ながら議論できるライセンスの費用と、操作を覚える時間。モデルがバイナリの形式になることが多く、差分の管理に工夫が要る
表計算ソフト一般的なスプレッドシート。乱数関数と集計機能を使う関係者の誰もが開いて中身を見られる。モンテカルロ程度の計算なら十分に足りる。導入の手間がない時間の概念と待ち行列の管理。規模が大きくなると計算が重くなり、式の依存関係が追えなくなる

この3つは優劣の順序ではありません。用途が違います。第3章で扱ったような、工期や費用の不確実性を積み上げるモンテカルロであれば、表計算で十分に成立します。むしろ表計算の最大の利点は、関係者が自分で開いて数字を触れることにあります。経営会議で「この前提を変えたらどうなるか」と問われたその場で数字を入れ替えられる価値は、機能の豊富さでは代替できません。

一方で、表計算が破綻する境目もはっきりしています。時間の流れと、資源の奪い合いを扱い始めた時点です。第4章で見たように、離散イベントシミュレーションの中核は、将来の事象を時刻順に並べて処理する仕組みにあります。この構造を表計算の行と列で表現しようとすると、行数が爆発するか、複雑なマクロを書くことになり、どちらにしても保守できなくなります。待ち行列が出てきたら、表計算からは離れる。これが実用的な境界線です。

専用シミュレータの価値は、機能の量そのものよりも、揃っていることにあります。搬送コンベアやフォークリフト、シフトカレンダー、故障と修理、段取り替えといった現場に固有の部品が用意されていると、モデル化の時間が大きく縮みます。同時に、動くアニメーションが出せることの効果は、技術的な話を超えています。現場の担当者は、数値の表よりも動く絵を見たときに「ここは実際と違う」と指摘してくれます。第10章で扱ったフェイスバリディティの確認が、圧倒的にやりやすくなります。

汎用言語で書く場合の利点は、拡張と接続の自由度です。シミュレーションの結果をそのまま統計処理に渡し、機械学習のモデルを組み込み、社内のデータベースから入力を読み、結果をダッシュボードに書き出す。こうした前後の工程が、同じ言語のなかで完結します。加えて、コードがテキストであることの意味は後述しますが、継続的に運用するモデルでは決定的に効いてきます。

縦軸に「自由度・拡張性」

代表的な製品を、分類の例として並べる

次に、具体的な名前を挙げておきます。ここで示すのは分類と得意分野までで、機能の詳細や費用については触れません。製品の仕様は改訂されますし、価格は契約形態によって変わります。検討に入る段階では、必ず提供元の最新の情報を確認してください。この表は、候補を探すときの入り口として使っていただくものです。

分類例として挙げられるものどんな問いに向くか
離散イベント、製造と物流に強い商用系Siemens Tecnomatix Plant Simulation、FlexSim、Arena、Simio生産ラインの編成、工程間のバッファ、倉庫のレイアウトと搬送、ヤードやバースの割り当てなど、物理的な設備とモノの流れが主役になる検討
マルチメソッドAnyLogic離散イベント、エージェント、システムダイナミクスを1つの環境で扱いたい場合。サプライチェーン全体のように、設備の詳細と市場の動きを同じモデルに載せたい検討
システムダイナミクス専用Vensim、Stella第7章で扱ったストックとフロー、フィードバックループの構造分析。政策や施策が長期でどう効くかを、因果ループ図から組み上げて見る検討
エージェントベースNetLogo、Mesa第6章で扱った個の行動から全体が立ち上がる現象。普及、口コミ、人流、選択行動など、主体どうしの相互作用が結果を左右する検討
Pythonのオープンソース離散イベントSimPy、salabim窓口や工程の待ち行列を、他の分析やデータ処理とつなげて扱いたい場合。小さく始めて継続的に育てる形の検討
数値計算と制御系のモデリングMATLAB と Simulink連続系の動特性、制御則の設計と検証。物理現象を微分方程式で記述し、制御装置と組み合わせて挙動を見る検討

この並びを見て気づいていただきたいのは、分類の境目がモデルの型と対応しているという点です。第2章で扱った4つの型は、そのまま道具の系統に対応します。だからこそ、型を先に決めてから道具を選ぶ順序が効きます。型が決まっていれば、候補は最初から絞られます。逆に型が決まっていない状態で製品を比較すると、比較の軸が定まらず、機能の多さだけで判断してしまいがちです。

マルチメソッドの位置づけには補足が要ります。1つの環境で複数の型を扱えることは、複数の型を同時に使うべきだという意味ではありません。第2章で述べたとおり、型は問いによって決まります。複数の型が本当に必要になるのは、たとえば「消費者の購買行動の変化が、工場の生産計画にどう波及するか」のように、性質の異なる現象が1本の因果でつながっている場合に限られます。そうした問いが実際にあるかどうかを先に確かめてください。

数値計算と制御系のモデリングは、この記事の主題からはやや外れます。連続系の微分方程式を扱う世界で、離散イベントとは前提が違います。ただし現場では両者が接する場面があります。設備そのものの動特性は連続系で、その設備が並んだラインの流れは離散イベント、といった具合です。第15章で扱うデジタルツインでは、この2つを組み合わせる構成もあります。

選定の判断軸を、要件の形で書き出す

候補が並んだら、次は比較です。機能の一覧を横に並べて丸をつける方式は、あまり役に立ちません。どの製品も主要な機能は持っているため、丸の数では差がつかないからです。差がつくのは、自分たちの組織の条件に照らした場合の使い勝手です。次の判断軸を、自分たちの答えとセットで書き出してみてください。

判断軸確かめるべきこと判断の目安
誰がモデルを保守するか作った人が異動したあと、誰が中身を直すのか。その人はプログラムを書けるのか非エンジニアが保守を担うなら、画面で組める専用シミュレータが有利。情報システム部門やデータ分析部門が持つならコード系でよい
アニメーションが要るか現場の担当者や経営層に、動きを見せて合意を取る必要があるか合意形成の場が重い案件ほど、アニメーションの価値が上がる。技術者だけで完結する検討なら優先度は下がる
他システムとのデータ連携入力を基幹システムや実績データから自動で取るのか、手で入れるのか。結果をどこへ返すのか自動連携が要るなら、接続の口が用意されているか、外部から起動できるかを確認する
実験と最適化の機能条件を振って何十回も反復する作業を、道具が管理してくれるか。探索の機能があるか第11章と第12章の内容を頻繁に使うなら、標準機能があると工数が減る。コード系なら自分で書く前提になる
再現性とバージョン管理モデルがテキストのコードか、バイナリのモデルファイルか。誰がいつ何を変えたか追えるかテキストなら差分が見え、変更履歴が残る。バイナリの場合は、変更の記録を人手の台帳で補う運用が要る
内製とベンダー依存モデルを自社で作るのか、外部に委託するのか。委託した場合、中身を自社で読めるか継続的に育てるつもりなら、中身を読める形にしておく。単発の検討なら委託でよい
教育コスト使える人を何人育てる必要があるか。その人たちの現在の技能は何かPythonを既に使う部署があるならコード系の教育費は小さい。そうでないなら画面操作の道具のほうが立ち上がりが早い

このなかで、検討の初期に見落とされやすいのが再現性とバージョン管理です。モデルは1回作って終わりにはなりません。前提が変わり、設備が増え、業務の手順が変わるたびに直します。1年後に「なぜこの数字になっているのか」と問われたとき、当時の版を再現できるかどうかが問われます。コードがテキストであれば、変更履歴を記録する一般的な仕組みにそのまま載ります。誰がいつどの行を変えたかが残り、以前の版に戻せます。

バイナリのモデルファイルの場合、この仕組みは使えません。ファイルを丸ごと保存することはできますが、2つの版の違いを機械的に比べることができないため、変更内容は人が記録するしかありません。これは製品の欠点というより形式の性質です。対策としては、変更のたびに「何をなぜ変えたか」を台帳に書く運用を最初から決めておくことになります。第16章で扱うモデル台帳の考え方が、ここに効いてきます。

コードで書けるかどうかという論点は、第15章で扱うデジタルツインのように継続運用する場合に、さらに重みを増します。継続運用とは、モデルが定期的に自動で実行され、実データを取り込み、結果が別のシステムに渡るという状態です。この形にするには、人が画面を操作しなくてもモデルが走る必要があります。テキストのコードであれば、スケジュール実行の仕組みに載せるだけで済みます。画面操作を前提とした道具でも外部から起動できる場合はありますが、そこは製品ごとに事情が違うので、継続運用を視野に入れるなら必ず事前に確認してください。

逆に言えば、単発の意思決定支援であれば、この論点はさほど重くありません。役員会に出す1回の検討のために、半年かけて自動化の仕組みを作る必要はありません。判断軸は、その道具でやろうとしていることの寿命に応じて重みを変えるべきものです。

SimPyの書き方、プロセス指向という書き味

ここからは実装の話に移ります。題材は SimPy です。Pythonの標準的な仕組みだけで離散イベントシミュレーションを書けるようにした軽量なライブラリで、外部の実行環境を必要とせず、通常のPythonのコードとして読めます。第4章で仕組みを説明した将来事象表の管理を、ライブラリの側が肩代わりしてくれます。

SimPyの特徴は プロセス指向 と呼ばれる書き方にあります。第4章で3つの流儀を挙げましたが、そのうちの1つです。エンティティ1つの立場に立って、その一生を上から順に書きます。「来店する、順番を待つ、窓口を使う、退店する」という手続きの流れが、そのままコードの縦の並びになります。事象の種類ごとに処理を書き分ける流儀と比べると、業務フローを説明する言葉との距離が近く、現場の担当者と突き合わせやすいという利点があります。

この書き方を実現しているのが、Pythonのジェネレータ関数です。ジェネレータとは、途中で処理を中断して呼び出し元に制御を返し、あとから続きを再開できる関数のことです。中断する場所を yield という語で示します。SimPyでは、この中断を「ここで時間が進む」あるいは「ここで資源が空くのを待つ」という意味に使います。書き手から見れば、待ちたい場所に yield を置くだけで、時計の管理はライブラリが引き受けます。

もう1つ、中核にあるのが環境の存在です。simpy.Environment() が作るオブジェクトは、シミュレーション上の時計と、これから起こる事象の予定表を持っています。第4章で手で追った将来事象表の実体がこれにあたります。この部分を 離散事象カーネル と呼びます。次に起こる事象を時刻順に取り出し、時計をその時刻まで進め、対応する処理を再開する。この繰り返しがシミュレーションの動力です。書き手はカーネルを直接触りませんが、内部で何が起きているかを知っておくと、想定と違う挙動になったときの切り分けが早くなります。

主要な語彙を先にまとめておきます。覚えるべきものは多くありません。

書き方意味
simpy.Environment()シミュレーションの環境を作る。時計と事象の予定表を持つ
env.timeout(t)時間を t だけ進める。作業にかかる時間を表現するのに使う
simpy.Resource(env, capacity=n)容量 n の資源を作る。窓口、機械、作業者、車両などにあたる
resource.request()資源を1つ要求する。空いていなければ、この時点で待たされる
env.process(f(env))新しい流れを走らせる。到着を作り続ける流れと、客1人ごとの流れの両方に使う
yield a | bab のどちらか早いほうを待つ。待ちきれずに帰る挙動などを表現できる
env.run(until=T)時刻 T まで走らせて止める
env.run()予定表が空になるまで走らせきる

コードを1つずつ読む

実際のコードを見ます。第4章で結果を扱った窓口配置の検討です。1分あたり0.9人が来店し、1件の処理時間は平均3分の対数正規分布、営業は8時間、客は平均10分の指数分布に従う我慢の限界を持ち、それを超えると帰ってしまいます。窓口の数を3から6まで振り、それぞれ200日分を試します。

import numpy as np
import simpy

ARRIVAL_RATE = 0.9          # 1分あたり0.9人が来店
SERVICE_MEAN = 3.0          # 1件あたり平均3分(対数正規)
SERVICE_SIGMA = 0.5
OPEN_MIN = 480              # 8時間営業
N_REP = 200

def run_day(n_staff, seed):
    rng = np.random.default_rng(seed)
    env = simpy.Environment()
    staff = simpy.Resource(env, capacity=n_staff)
    waits, abandoned = [], []

    def customer(env):
        t0 = env.now
        patience = rng.exponential(10.0)        # 待てる時間
        with staff.request() as req:
            res = yield req | env.timeout(patience)
            if req in res:
                waits.append(env.now - t0)
                mu = np.log(SERVICE_MEAN) - SERVICE_SIGMA ** 2 / 2
                yield env.timeout(rng.lognormal(mu, SERVICE_SIGMA))
            else:
                abandoned.append(1)

    def source(env):
        while True:
            yield env.timeout(rng.exponential(1 / ARRIVAL_RATE))
            if env.now > OPEN_MIN:
                return
            env.process(customer(env))

    env.process(source(env))
    env.run()
    n = len(waits) + len(abandoned)
    # 1日ぶんの「平均待ち」と「客ごとの待ち時間の95%点」と「離脱率」を返す
    if not waits:
        return 0.0, 0.0, len(abandoned) / n * 100
    return np.mean(waits), np.percentile(waits, 95), len(abandoned) / n * 100

print("%-4s %-14s %-20s %-10s"
      % ("窓口", "平均待ち(分)", "客の待ちの95%点(分)", "離脱率(%)"))
for c in (3, 4, 5, 6):
    res = np.array([run_day(c, 10_000 + s) for s in range(N_REP)])
    print("%-4d %-14.2f %-20.2f %-10.2f"
          % (c, res[:, 0].mean(), res[:, 1].mean(), res[:, 2].mean()))

順に見ていきます。まず run_day という関数が、1日分の営業を1回まわす単位になっています。引数は窓口の人数と乱数の種の2つです。第9章で扱った反復は、この関数を種を変えて何度も呼ぶことで実現されます。1回の実行を関数に閉じ込めておくと、反復も、条件の振り替えも、あとで並列に走らせることも、すべて同じ形で書けます。

関数の冒頭3行が、環境の準備です。np.random.default_rng(seed) で乱数生成器を作り、simpy.Environment() で環境を作り、simpy.Resource(env, capacity=n_staff) で窓口を用意します。容量を引数で受け取っているので、3人体制と5人体制の違いは、この1つの数値だけで表現されます。実務のモデルでも、比べたい条件はこのようにパラメータとして外に出しておくと、実験が楽になります。

waitsabandoned は、結果を貯めるための空のリストです。シミュレーションの最中は何も集計せず、起きたことをそのまま記録していきます。集計は走り終わってから行います。この順序にしておくと、あとから「95%点も見たい」と思ったときに、記録済みのリストから計算できます。実行中に平均だけを更新していく書き方にしてしまうと、あとから分位点を出したくなったときに走らせ直すことになります。

次が customer 関数、客1人の一生です。ここがプロセス指向の中心にあたります。t0 = env.now で来店時刻を記録し、patience で我慢の限界を引きます。env.now はシミュレーション上の現在時刻で、実際の時計ではなくモデル内部の時計を返します。

そのあとの with staff.request() as req: が、窓口を要求する部分です。with という書き方を使っているのは、ブロックを抜けたときに自動で資源を返してもらうためです。Pythonでファイルを開くときに使うのと同じ仕組みで、途中で処理が飛んでも確実に解放されます。資源の解放を書き忘れると、窓口が使用中のまま残り、以降の客が永久に待つという状態になります。この種の不具合は結果を見ても気づきにくいので、自動で返る書き方を使う意味は大きいと感じています。

その次の行が、この記事のなかでも特徴的な書き方です。

res = yield req | env.timeout(patience)
if req in res:

縦棒の記号は、2つの待ちのうちどちらか早いほうを待つという意味です。左側は窓口が取れること、右側は我慢の限界が来ることを表します。窓口が先に空けば、客はサービスを受けます。我慢の限界が先に来れば、客は帰ります。res には、どちらが先に成立したかが入るので、if req in res で分岐できます。行列を作って途中で帰る客を、たった2行で表現できていることになります。

サービスを受ける側の分岐では、待ち時間を記録し、対数正規分布から処理時間を引いて env.timeout で時計を進めます。mu の計算は、対数正規分布の平均を SERVICE_MEAN に合わせるための変換です。対数正規分布のパラメータは対数を取った世界での平均と標準偏差なので、元の世界での平均を指定したい場合はこの補正が要ります。第8章で扱った入力モデリングの実装上の注意点の1つです。

source 関数は、客を作り続ける流れです。無限ループのなかで、まず到着間隔だけ時間を進め、それから新しい客の流れを起動します。env.process(customer(env)) がその起動にあたり、これによって客ごとの流れが独立して走り始めます。営業時間を過ぎたら return で生成をやめます。ここで生成をやめても、既に来店している客の処理は続きます。閉店時刻に扉を閉めるが、中にいる客の対応は終わるまで続ける、という現実の運用と同じ挙動です。

関数の末尾にある env.run() が実行の開始です。引数を付けていないので、予定表が空になるまで走らせきります。この書き方であれば、閉店後に残った客が最後まで処理されてから止まります。もし env.run(until=480) と書けば、480分の時点で強制的に止まるため、処理途中の客が集計から漏れます。どちらが正しいかは目的によりますが、この検討では離脱率を正確に数えたいので、走らせきる形が適しています。この違いは結果に効くので、意識して選ぶべき箇所です。

関数の末尾が集計です。return np.mean(waits), np.percentile(waits, 95), len(abandoned) / n * 100 という行で、1日ぶんの結果として3つの値を返しています。待ち時間の平均、客ごとの待ち時間の95%点、離脱率です。その手前の if not waits の分岐は、1人もサービスを受けられなかった極端な場合に、空のリストから平均や分位点を計算しようとしてエラーになるのを防ぐためのものです。実務のモデルでも、条件を極端に振ったときに空のリストを集計しようとして止まる事故はよく起きます。

この3つを1つの関数の中でまとめて計算していることには、見た目以上の意味があります。測りたい指標は、記録を持っている場所で全部計算して返しておく、という作法の実例になっているからです。第9章で述べたとおり、実行し終えてから「やっぱり95%点も見たい」となると、原則として走らせ直しになります。しかしこの作法が守る対象は、走らせ直しの手間だけではありません。もっと厄介な取り違えを防ぎます。

仮に、この関数が平均待ち時間と離脱率の2つだけを返す設計だったとします。95%点が欲しくなったとき、手元にあるのは200日ぶんの「その日の平均待ち時間」が並んだ配列です。ここに分位点の計算をかけると、数値は問題なく出ます。しかしそれは客の待ち時間の95%点ではなく、日別平均の95%点です。日別平均は1日ぶんを平均した時点でばらつきが均されているため、個々の客の待ち時間の分布よりはるかに内側に寄ります。両者は別の量であり、置き換えられません。

この誤りが危険なのは、出力が壊れないところにあります。エラーも警告も出ず、桁もそれらしく、条件を振れば単調に動きます。表に「95%点」と書かれていれば、読む側は客の待ち時間の分位点だと受け取ります。サービス水準の約束を作るために使えば、実際より甘い数値で約束を結ぶことになります。集計の場所が記録から遠いほど、この種の取り違えは起きやすくなります。run_day の内側で waits から直接計算していれば、対象を間違えようがありません。

関連して、出力の列名にも同じ配慮が要ります。ここで見出しを「待ち95%点」ではなく「客の待ちの95%点」としているのは、何の分位点なのかを出力そのものに残すためです。シミュレーションの出力は、集計の単位が複数あるのが普通です。客ごと、日ごと、反復ごと、時間帯ごと。同じ「平均」「95%点」という語が、単位を変えるだけで別の量を指します。列名に単位を書いておくと、後で読み返したときや、他人に渡したときの誤読が減ります。

関数の外側では、窓口数を3から6まで振り、それぞれ200回の反復を回して集計しています。種を 10_000 + s としているのは、実行するたびに同じ結果が出るようにするためです。第10章で扱った検証の観点から、再現できることは必須の条件になります。

実行結果は次のとおりです。

窓口   平均待ち(分)        客の待ちの95%点(分)         離脱率(%)
3    1.02           3.82                 10.60
4    0.34           1.89                 3.54
5    0.11           0.83                 1.19
6    0.03           0.14                 0.34

数値の読み方は第4章で扱ったとおりです。ここで注目していただきたいのは、コードの分量のほうです。空行とコメントを含めて48行、実質的なモデルの記述は20行ほどです。この分量で、確率的な到着、対数正規の処理時間、複数窓口の奪い合い、途中離脱、200回の反復、条件の振り替えまでが表現されています。専用のツールを導入しなくても、この規模の検討であればPythonの標準的な環境で書き切れます。

Pythonで組むときの実務的な注意

コード自体は短く書けますが、実務で使うモデルに育てるときには、いくつか守っておくと後で楽になる作法があります。ここまでの章で扱った内容と対応させながら挙げておきます。

  • 乱数生成器を用途別に分ける。到着間隔用、処理時間用、故障用というように、別々の生成器を用意します。第11章で扱った共通乱数法を使うには、この分離が前提になります。1本の生成器をすべての用途で共有していると、条件を変えたときに乱数の消費順序がずれ、案どうしの対応が崩れます
  • 結果はリストに貯めて後で集計する。実行中に平均を更新していく書き方は避けます。生の記録を残しておけば、分位点も、時間帯別の内訳も、あとから何度でも計算できます
  • 測りたい指標は、記録を持っている場所で全部計算して返す。平均だけを返しておいて、あとから外側で分位点を計算すると、集計の単位が1段ずれた別の量を出してしまいます。しかもエラーは出ないので気づけません。返す値の名前と出力の列名に、何を何の単位で測ったかを書き込んでおきます
  • 1回の実行を関数にする。上のコードの run_day がその形です。関数になっていれば、反復も条件の振り替えも同じ書き方で書けますし、複数のコアで並列に実行する形にも移しやすくなります。反復のあいだに状態が共有されていないことが、並列化の条件です
  • モデルとパラメータを分ける。到着率や処理時間の設定値をコードの中に直接書き込まず、外部のファイルから読む形にします。前提を変えるたびにコードを書き換えていると、どの版でどの前提を使ったかが分からなくなります。第16章で扱う適用範囲の宣言も、パラメータが外に出ていれば書きやすくなります
  • テストを書く。第10章で扱った検証にあたります。理論値が計算できる単純な条件を用意し、モデルがその値を再現することを自動で確かめる仕組みを作っておきます。モデルを改造したときに、既存の挙動が壊れていないかを毎回確認できます

このうち、最後のテストについて補足します。シミュレーションのモデルは確率的に動くので、通常のプログラムのように「この入力ならこの出力」という形の検査はできません。代わりに、種を固定して同じ結果が出ることを確かめる検査と、統計的な性質が理屈と合うことを確かめる検査を組み合わせます。後者は、たとえば窓口を1つにして到着も処理時間も指数分布にすれば、平均待ち時間の理論値と突き合わせられます。第4章で示した突き合わせを、自動で走る形にしておくということです。

もう1つ、実行速度についての注意があります。Pythonは実行速度の面では有利な言語ではありません。エンティティが数十万を超えるような規模になると、実行時間が現実的でなくなる場合があります。対策としては、モデルの粒度を落とす、必要な部分だけを詳細に作る、反復を並列に走らせる、といった手が取れます。それでも足りないなら、そこが専用シミュレータや別の実装を検討する境目になります。ただし、規模の問題に直面する前にモデルの前提のほうを見直したほうがよい場合も多く、いきなり速度の話に進むのは早計です。

並列実行については、反復ごとに独立していることが条件になると述べました。上のコードでは、run_day の中で環境も生成器もリストもすべて作り直しているので、この条件を満たしています。逆に、関数の外側に置いた変数に結果を書き込むような書き方をしていると、並列にしたときに結果が混ざります。最初から並列化を意識して書いておくと、反復回数を増やしたくなったときに書き換えが要りません。

どの道具でも変わらないこと

最後に、この章全体に対する留保を置いておきます。ここまで道具の分類と選定の軸を並べてきましたが、道具を変えても省略できない工程があります。第8章から第11章で扱った内容です。

入力の質は道具に依存しません。どれほど高機能なシミュレータを使っても、処理時間の分布を根拠なく決めれば、出てくる数値に根拠はありません。第8章で扱ったように、データから分布を決める作業、データがない場合に現場の知見から仮の分布を置く作業、その仮定が結果にどれだけ効くかを確かめる作業は、道具に関係なく必要です。むしろ、道具が立派であるほど出力が信頼できるように見えてしまうぶん、注意が要ります。

検証と妥当性確認も道具に依存しません。第10章で扱ったとおり、モデルが意図どおりに動いているかの確認と、そのモデルが現実を十分に表しているかの確認は、別々の手続きです。専用シミュレータには検証を助ける機能が付いていることがありますが、機能があることと手続きを踏むことは別です。極端な条件で挙動を見る、理論値と突き合わせる、現場に見てもらう。この3つは、どの道具でも自分でやる必要があります。

出力の統計的な読み方も同じです。第9章で見たように、シミュレーションの結果は推定値であり、区間を伴います。何回反復したのか、区間はどれだけの幅か、立ち上がり期間を捨てたか。これらを確認しないまま1本の数値を報告すれば、道具が何であっても結論は危ういままです。専用シミュレータの実験機能は反復の管理を助けてくれますが、何回まわすべきかを決めるのは人です。

したがって、道具の選定に費やす時間と、これらの工程に費やす時間の配分を間違えないことが大切です。製品比較に3か月をかけ、データの整備に2週間しかかけないという配分は、順序が逆です。データの整備と検証にかかる時間を先に見積もり、そのうえで残りの制約から道具を決める。この順序であれば、少なくとも道具のせいで失敗することはなくなります。

次の第15章では、ここで作ったモデルを現場のデータにつなぎ、継続的に動かし続ける形について扱います。単発の検討から運用へ移るとき、この章で触れた「コードで書けるか」「外部から起動できるか」「バージョンを管理できるか」といった論点が、実際の制約として現れてきます。

左に「道具の選定」

この章を深めたい方への参考書籍

Pythonによるシミュレーションモデリング(Giuseppe Ciaburro 著、黒川利明 訳、朝倉書店)
Pythonでシミュレーションを組む手順をまとめた本です。本章で紹介した道具立てを、自分の環境で動かしてみるときの手引きになります。

シミュレーション(白鳥則郎 監修、佐藤文明・齊藤稔・石原進・渡邊尚 著、共立出版)
モデル化から乱数生成、待ち行列モデル、微分方程式、セルオートマトン、マルチエージェントまでを一続きで扱う教科書です。本コラムで型ごとに分けて説明した内容を、体系立てて読み直せます。

第15章 デジタルツイン、モデルを現場につなぐ

ここまでの14章で扱ってきたのは、いずれも「一度きりの調査」としてのシミュレーションでした。問いを立て、データから分布を決め、モデルを組み、必要な回数だけ回し、結果を比べ、報告書にまとめる。終わればモデルはファイルとして残り、次に似た問いが来るまで開かれません。この形は投資判断やレイアウトの検討には向いていますが、日々動いている現場の判断には届きません。判断が必要になるのは会議の前日ではなく、今日の午後だからです。

本章で扱うのは、その距離を埋めるための形です。モデルを現実の対象とつなぎ、データが流れ込み続ける状態にして、日常の判断に使えるようにする。この形を指す言葉として広く使われているのが、デジタルツインです。

この言葉は、期待が先行して意味が薄まりやすい言葉でもあります。3次元の画面が動いていればデジタルツインだと呼ばれることもあれば、単にセンサーの値をグラフにしただけのものがそう呼ばれることもあります。呼び方をめぐって争っても得るものはありませんが、何が違うと何ができるようになるのかを構造として理解しておかないと、投資の判断ができません。本章では、流行語としてではなく、これまでの章で積み上げてきたシミュレーションの延長線上にある構造として説明します。

定義から入る、違いは同期の有無にある

デジタルツインとは、現実に存在する対象に対応するモデルを計算機の中に持ち、実データによって継続的に同期させ、その対象の状態把握・予測・評価に使う仕組みを指します。対象は設備1台のこともあれば、生産ラインや倉庫、配送網、建物、都市のこともあります。分野によって扱う物理量も、使うモデルの型も違いますが、構造としては共通しています。

従来のシミュレーションとの違いは1点に集約できます。モデルが現実とつながり続けているかどうかです。第4章で作った離散イベントのモデルは、過去の実績データから分布を推定し、そのパラメータを固定して回しました。回し終えた時点で、モデルと現実の関係は切れています。翌週に現場で新しい設備が入っても、モデルは何も知りません。デジタルツインでは、この接続が切れずに維持されます。現実の状態が変われば、モデルの中の状態も変わります。

もう1つ、目的の違いも押さえておく必要があります。一度きりの調査では、「AとBのどちらが良いか」という比較が目的でした。デジタルツインでは、比較に加えて「いま現実がどうなっているか」「このまま行くとどうなるか」という現在と近未来の把握が目的に入ります。同じモデルでも、問いが変われば要求される精度も速度も変わります。年に一度の投資判断なら計算に一晩かけても構いませんが、今日の午後の配車を決めるなら、10分で答えが出なければ使えません。

この構造を一言でいえば、現実の側と計算機の側の間で情報を往復させる仕組みだということになります。現実の側を物理空間、計算機の側を仮想空間と呼ぶ整理が広く使われており、このフィジカルとサイバーの往復をどこまで自動化できているかが、後述する段階の違いを生みます。往復のうち片道しか通っていない状態と、両方向が通っている状態では、できることがはっきり分かれます。

3つの段階、多くの取り組みは途中にある

この分野では、接続の度合いによって3つの段階を区別する整理が文献で広く用いられています。デジタルモデル、デジタルシャドウ、デジタルツインの3つです。区別の基準は、データが自動で流れるかどうかと、その向きです。

デジタルモデルは、現実の対象に対応するモデルを持っていますが、データのやり取りは手作業です。担当者が実績データを取り出し、加工し、モデルに読み込ませます。結果も人が読み、人が判断に使います。本コラムでここまで扱ってきたシミュレーションは、すべてこの段階にあたります。手作業だから劣っているという話ではありません。問いが年単位で変わる用途なら、この段階で十分に目的を果たします。

デジタルシャドウは、現実からモデルへ、片方向で自動的にデータが流れます。センサーや制御機器や生産管理システムから値が取り込まれ、モデルの中の状態が現実に追随します。モデルは現実を映しますが、モデルから現実へは働きかけません。計算した結果は人が見て、人が判断します。影という呼び名は、対象の動きに追随はするが、対象に影響は与えないという関係を表しています。

デジタルツインは、双方向です。モデルの計算結果が、現実の制御や判断に自動で返ります。設定値の変更、指示の発行、警報の発出、作業順序の組み替えといった形で、計算機の中の判断が現実の側の動作になります。人が最終確認する形を挟むかどうかは設計次第ですが、少なくとも仕組みとしての経路が用意されている点が、シャドウとの違いです。

段階データの流れできること典型的な使われ方難所
デジタルモデル手作業。人がデータを入れ、人が結果を読む設計案の比較、投資判断、方針の検討プロジェクト単位の検討、報告書作り直しの手間。データ準備が毎回発生する
デジタルシャドウ現実からモデルへ片方向で自動現状の把握、異常の検知、先読みの提示監視画面、日次や時間単位の見通しデータ取得経路の整備、時刻の整合
デジタルツイン双方向。結果が現実の制御や判断に返る自動での調整、条件変化への追随設定値の自動変更、指示の自動発行誤りが事故になる。責任と権限の設計

この整理を持っておくと、事例の読み方が変わります。デジタルツインとして紹介されている取り組みの多くは、構造としては実際にはシャドウの段階にあります。データは自動で入ってくるが、そこから先の判断は人が下している、という形です。これは批判ではありません。シャドウの段階でも、現状把握と先読みができるだけで実務上の価値は十分にあり、しかも双方向にすることで生じる責任の問題を抱え込まずに済みます。

大事なのは、名乗りではなく実態を把握することです。自社の取り組みがどの段階にあるかを正確に言えれば、次に何を整備すればよいかが決まります。逆に、シャドウの段階にあるものをツインと呼んでしまうと、「もう完成している」という誤解が生まれ、実際には未整備の部分に予算がつかなくなります。社外に説明するときの呼び方と、社内の設計議論で使う呼び方は、分けて考えたほうが安全です。

デジタルモデル・デジタルシャドウ・デジタルツインの3段階を表す概念図

ツインを成り立たせるために何が要るか

接続を維持するという要件は、言葉にすると1行ですが、実現するには複数の要素が揃っている必要があります。どれか1つが欠けると、全体が動きません。順に見ていきます。

第1に、対象の識別です。何をツインの単位にするかを決めます。設備1台なのか、工程なのか、ライン全体なのか、工場全体なのか。単位が小さいほどモデルは作りやすく、データも揃えやすく、検証もしやすくなります。単位が大きいほど、扱える問いは広がりますが、必要なデータの種類が増え、どこか1か所のデータが欠けただけで全体が止まります。最初に大きく取りたくなりますが、実務上は小さく始めるほうが立ち上がります。

第2に、データの取得経路です。センサー、制御機器、生産管理システム、位置情報、点検記録、外部の気象情報など、モデルが必要とする値がどこから来るかを1つずつ特定します。ここで問題になるのは、たいていの場合、値が存在しないことではなく、値は存在するが取り出せる形になっていないことです。画面には出ているが外部から読む口がない、帳票にはあるが手書きである、といった状態がよくあります。

第3に、時刻の整合です。これは軽視されやすく、そして最も詰まりやすい要素です。複数の機器やシステムから来たデータを1つのモデルに流し込むとき、それぞれのタイムスタンプが揃っていないと、同期になりません。機器の内蔵時計が数分ずれている、システムごとに記録している時刻の意味が違う、夏時間や時間帯の扱いが揃っていない、記録の粒度が秒と分で混ざっている。こうしたずれは、個々のデータを眺めていても気づけません。突き合わせた瞬間に、原因不明の矛盾として現れます。時刻の基準を決め、揃っていることを継続的に確認する仕組みを、最初から設計に入れておく必要があります。

第4に、モデルの更新です。取り込んだデータから、モデルのパラメータを再推定する仕組みが要ります。第8章で扱った入力モデリングを、一度きりではなく定期的に回すということです。処理時間の分布、故障の発生率、需要の水準といったパラメータは、季節や製品構成や設備の状態によって変わります。この再推定を自動で行い、変化が大きいときには通知する形にしておきます。

第5に、計算速度です。先読みに使うなら、モデルは実時間より速く回らなければ意味がありません。1時間先を予測するのに1時間かかるモデルは、予測ではなく実況です。しかも、第9章で扱ったように、乱数を使うモデルでは1回の実行では足りず、必要な反復回数を確保する必要があります。判断までに使える時間の中で、必要な反復回数を回しきれる速度が要件になります。ここが満たせない場合、モデルを簡略化するか、あらかじめ計算した結果を近似式に置き換えるか、判断の周期を長く取り直すかの選択になります。

第6に、可視化と通知です。誰が、どの画面で、何を見て、何を判断するのかを決めます。ここが曖昧なままだと、計算はされているが誰も見ていない状態になります。閾値を超えたときに誰にどう伝えるかまで含めて設計します。

第7に、権限と安全です。モデルの計算結果が現実の制御に返るなら、モデルの誤りは事故になります。どの範囲までを自動で変えてよいか、変えてよい値の上限と下限、異常時に自動制御を切り離す手段、誰が止められるか。これらを決めずに双方向にしてはいけません。シャドウの段階にとどめる判断が合理的な場面は多く、その判断はこの要素の重さから来ます。

同期の設計、意思決定の周期に粒度を合わせる

デジタルツインで最も設計の質が問われるのが、同期の設計です。何を、どの頻度で、どこまで合わせるかを決めます。ここで陥りやすいのは、すべてをリアルタイムで合わせようとすることです。技術的にできるかどうかと、そうすべきかどうかは別の問題です。

判断の基準は単純です。その情報を使って下す意思決定の周期に、同期の粒度を合わせます。日次で立てる生産計画に使うなら、日次で同期すれば足ります。1分ごとに更新しても、判断は1日1回しか行われないのですから、残りの1439回分の計算と通信は使われません。逆に、数秒で応答が必要な制御に使うなら、秒単位の同期が要ります。

頻度を上げすぎることの害は、計算資源の無駄だけではありません。通信量が増え、蓄積するデータ量が増え、障害の発生確率が上がり、監視すべき対象が増えます。運用の負荷が増えた結果、面倒になって誰も見なくなり、止まっていることに気づかないまま放置される、という形で破綻します。同期の頻度は、運用を続けられる水準に落とす設計判断が必要です。

意思決定の周期典型的な判断同期の粒度の目安要求される計算速度段階の目安
年から四半期設備投資、レイアウト変更、拠点の配置その都度、実績データを取り込む一晩かけてよいデジタルモデルで足りる
月から週要員計画、在庫方針の見直し、保全計画週次または日次のバッチ数時間以内モデルからシャドウへ
日次翌日の生産計画、配車、シフトの確定日次。締め時刻を決めて取り込む1時間以内シャドウで実用になる
時間単位作業の割り当て変更、在庫の引き当て1時間ごと、または事象が起きるたび数分以内シャドウ、一部ツイン
分から秒設備の設定値変更、搬送の経路切り替え連続。事象駆動で即時秒単位ツインが必要

この表の使い方は、上から下へ読むのではなく、まず自社の判断がどの行にあるかを特定することです。行が決まれば、必要な同期の粒度と計算速度が決まり、目指すべき段階も決まります。多くの業務上の判断は上の3行に入ります。つまり、シャドウの段階で目的を果たせるということです。最下行を目指す必要があるのは、判断の周期が人の反応速度より速い領域に限られます。

もう1つ、同期の範囲についての判断もあります。モデルの中のすべての状態を現実と合わせる必要はありません。結論に効く状態だけを合わせ、それ以外は分布から生成するという設計が現実的です。たとえば、各設備の稼働状態と仕掛品の位置は実データで合わせるが、これから到着する注文は過去の分布から生成する、という形です。現在は実データ、将来は分布という切り分けは、多くの用途で自然に機能します。

中身は既存の型、そして機械学習とは分業になる

第2章で、シミュレーションの型としてモンテカルロ、離散イベント、エージェントベース、システムダイナミクスの4つを整理しました。デジタルツインは、この4つに並ぶ5つ目の型ではありません。既存の型に、同期と継続運用という2つの性質を足したものです。

中身が何であるかは、対象によって変わります。工場の生産ラインなら、中身は離散イベントのモデルです。設備の物理的な挙動を扱うなら、物理法則に基づく数値解析のモデルです。人や車の動きを扱うなら、エージェントベースのモデルです。需要と在庫の中長期的な振る舞いなら、システムダイナミクスのモデルです。複数を組み合わせることもあります。デジタルツインという言葉は、これらの中身を指しているのではなく、外側の運用形態を指しています。

この理解は投資判断に直結します。デジタルツインを導入するという計画があったとして、その中身にあたるモデルが作れていなければ、同期の仕組みだけ作っても何も計算できません。逆に、モデルが作れているなら、そこに同期を足すのは追加の工事です。順序としては、モデルが先で、同期が後になります。

もう1つ、中身について整理しておくべき点があります。デジタルツインの文脈では、機械学習による予測モデルとシミュレーションが対立する選択肢のように語られることがありますが、実際には役割が分かれており、組み合わせて使うのが自然です。

機械学習が得意なのは、センサーから得られる大量の値をもとに、直接は測れない状態を推定することです。振動と温度と電流の波形から設備の劣化度合いを推定する、画像から製品の状態を判別する、複数の指標から異常の兆候を捉える。こうした処理は、物理法則から式を立てるより、データから関係を学習させるほうが早く精度も出ます。

シミュレーションが得意なのは、推定された状態を出発点にして、この後どうなるか、この手を打つとどうなるかを計算することです。設備の劣化度合いが分かったとして、このまま運転を続けた場合に納期にどう影響するか、いま止めて保全に入った場合に他の工程がどう詰まるか。これは実際には起きていない状況なので、学習させるデータが存在しません。第1章で述べたとおり、シミュレーションは起きていないことを計算できるという点に価値があります。

つまり、現在の状態の推定に機械学習を使い、そこから先の展開の計算にシミュレーションを使う、という分業になります。前者が後者の初期状態を作り、後者が前者では扱えない仮定の話に答えます。どちらか一方を選ぶ問題ではありません。加えて、シミュレーションの出力を学習データとして機械学習の予測モデルを作る使い方もあり、これは第12章で扱ったメタモデルの考え方をそのまま持ち込んだものです。計算に時間がかかるモデルの応答を近似式に置き換えることで、実時間での応答が必要な場面に対応できるようになります。

段階的な進め方と、そこで待っている落とし穴

導入の順序について、実務上ほぼ確立している考え方があります。小さく作り、動かし続けられることを確認してから、範囲を広げるという順序です。

段階やることこの段階で確認すること次に進む条件
①モデルを作る1つの設備、1つの工程に絞ってモデルを作る第10章の検証と妥当性確認を通せるか現場の担当者が結果に納得しているか
②定期検証の仕組み実績データとモデルの出力を定期的に突き合わせるずれの大きさと、ずれが出る条件ずれが許容範囲に収まり続けているか
③シャドウ化データ取得を自動化し、片方向の流れを作るデータの欠損率、時刻の整合、障害時の挙動手作業なしで数週間動き続けたか
④同期の運用意思決定の周期に合わせて回し、結果を画面に出す実際に見られているか、判断に使われているか止まったときに気づく仕組みがあるか
⑤限定した制御範囲を限って、計算結果を現実に返す誤った場合の影響範囲、切り離す手段安全側に倒す設計が合意されているか

この順序で重要なのは、各段階の「次に進む条件」です。前の段階が安定していないまま次に進むと、問題の切り分けができなくなります。モデルの妥当性が確認できていない状態で自動化すると、出てくる数字がおかしいときに、モデルが悪いのかデータ取得が悪いのか分かりません。③を数週間動かすという条件も同様で、この期間に、休日の扱い、月末の処理、通信の一時的な断絶、機器の再起動といった、平常時には現れない事象がひととおり出てきます。

いきなり全社規模のツインを構想する計画は、①と②を飛ばして③から始めることになりがちです。データ基盤の構築から入り、全設備からデータを集める仕組みを何か月もかけて作り、集め終わったところで、そのデータを使って何を計算するのかが決まっていないことに気づく、という筋道です。順序を逆にして、1つの工程で使えるモデルを作り、そのモデルが必要とするデータだけを取りに行くほうが、はるかに早く価値が出ます。

この順序を踏んでいても、途中で止まる取り組みはあります。この領域で報告されている失敗には、繰り返し現れる型があります。あらかじめ知っておくと、計画の段階で避けられるものが多くあります。

  • データ基盤の整備が目的化する。基盤を作ること自体が目標になり、モデルが作られないまま予算と期間を使い切ります。集めたデータは、使われなければ保管費用が発生するだけです。何を計算するために何のデータが要るかを先に決めます。
  • 3次元の見た目に投資が寄る。動く画面は説得力があり、経営層への説明にも使いやすいため、そこに費用が集中しがちです。ところが、見た目の精緻さと意思決定への有用性は比例しません。画面が現実そっくりでも、そこから何を判断するのかが決まっていなければ、確認画面以上のものにはなりません。
  • 同期の頻度を上げすぎて運用が破綻する。必要のない頻度で回した結果、通信と保存の負荷が増え、障害対応の手間が増え、続けられなくなります。判断の周期に合わせる設計に戻します。
  • 妥当性確認を継続する体制がない。第10章の手続きを構築時に一度だけ行い、その後は行わない場合、モデルはいつの間にか現実からずれていきます。ずれていることに誰も気づかないまま、数字だけが出続けます。
  • 現場の変更がモデルに反映されない。設備が更新された、レイアウトが変わった、作業手順が見直された。こうした変更がモデルに伝わる経路がないと、モデルだけが古い現実を映し続けます。データは今日のものが流れ込んでいるのに、構造は2年前のままという状態は、外から見て気づきにくいのが厄介なところです。
  • 誰が保守するのかが決まっていない。構築を外部に委託した場合に特に起きます。納品後、パラメータの更新も、変更の反映も、障害対応も、担当者が不在のまま放置されます。運用の担当と予算を、構築の計画に最初から含めておく必要があります。

これらを並べてみると、共通する構造が見えてきます。失敗のほとんどは、作るときの問題ではなく、続けるときの問題です。デジタルツインは構築物ではなく運用の仕組みなので、構築の予算だけを確保して運用の予算を確保しない計画は、この時点ですでに失敗が織り込まれています。

段階的な導入の順序と落とし穴を表すフロー図

モデルの劣化、同期していても構造は古びる

最後に、この章で最も見落とされやすい論点を独立して扱います。同期していれば、モデルは現実に追随し続けると考えたくなりますが、これは正しくありません。同期が合わせているのは状態とパラメータであって、モデルの構造そのものではないからです。

モデルの構造とは、どの要素を入れ、どの要素を無視し、要素の間にどんな関係を置いたか、という設計上の判断の集まりです。工程の順序、資源の種類と数、優先順位の規則、分岐の条件、扱う範囲の境界。これらは構築時に人が決めたものであり、データが流れ込んできても自動では変わりません。現実の側でこれらが変わったとき、モデルは静かにずれ始めます。

しかも、この種のずれは発見が遅れます。パラメータのずれなら、実績との比較で数値の差として現れます。構造のずれは、平均値のような集計指標には出にくく、特定の条件のときだけ大きく外れるという形で現れます。全体としては当たっているように見えるので、外れた回を例外として処理してしまい、蓄積してから初めて気づくことになります。

対策は2つあります。1つは、現実の側の変更を、モデルの保守に伝える経路を作ることです。設備の更新、レイアウトの変更、手順書の改訂といった変更が起きたときに、モデルの担当者に通知が届き、影響の有無を判定する。この経路は組織の運用として作るしかなく、技術で自動化できる部分は多くありません。

もう1つは、妥当性確認を定期的な業務として設計することです。第10章で扱った手続きを、構築時の一度きりのイベントではなく、四半期に一度、あるいは半期に一度といった周期で実施する業務にします。実施の内容は構築時と同じで、実績データとの突き合わせ、極端な条件での挙動確認、現場の担当者による確認です。周期を決め、担当を決め、結果を記録に残します。記録が積み上がると、ずれが拡大しているのか安定しているのかが判定できるようになります。

この定期確認の結果として、モデルを作り直す判断が出ることもあります。それは失敗ではなく、想定された寿命に達したということです。現実の対象が変わり続ける以上、モデルにも寿命があります。作り直す前提で設計しておけば、作り直しの費用と期間をあらかじめ計画に含められます。この考え方は、モデルを資産として管理するという発想につながり、次章で扱う運用の設計の一部になります。

定義は標準化されている、ただし範囲は限られる

ここまでは考え方として説明してきましたが、デジタルツインには国際規格による定義があります。製造業を対象とした規格として、ISO 23247シリーズ「Automation systems and integration, Digital twin framework for manufacturing」が定められています。第1部の初版は2021年10月の発行で、策定はISOのTC 184、industrial dataを扱うSC 4が担当しています。

副題発行年
Part 1Overview and general principles2021年
Part 2Reference architecture2021年
Part 3Digital representation of manufacturing elements2021年
Part 4Information exchange2021年
Part 5Digital thread for digital twin2026年
Part 6Digital twin composition2026年

第1部が定めている定義は、目的に適合した対象のデジタル表現であって、対象とそのデジタル表現のあいだに同期があるもの、という趣旨のものです。本章の冒頭で述べた「同期しているかどうかが分かれ目である」という整理は、規格の定義とも一致しています。

規格が対象とする「観測可能な製造要素」の範囲も、押さえておく価値があります。規格は、設備、材料、工程、施設、環境、製品、関連文書とともに、人員を明示的に含めています。デジタルツインという言葉から機械や設備だけを思い浮かべがちですが、規格の想定はもっと広いということです。第13章で触れた人員計画や技能の話も、この枠の中に入りえます。

参照アーキテクチャは4つの層で構成されます。観測可能な製造要素、機器との通信を担う層、デジタルツインの本体、そして利用者側の層です。データを取る仕組みとモデル本体と使い手を分けて設計する、という構えは、社内で役割分担を決めるときの下敷きになります。

あわせて、規格が何を定めていないかも重要です。第1部は、この文書はフレームワークであり、具体的なデータ形式や通信プロトコルは規定しない、と明記しています。つまり、規格に従えば相互接続が保証されるわけではありません。データ形式は、用途に応じて別の規格を組み合わせることになります。規格があること自体は、実装が楽になることを意味しないという点は、導入計画を立てるときに見誤りやすいところです。

業界団体と学術機関の定義、そして市場の数字の読み方

規格以外にも、広く参照されている定義があります。業界団体であるDigital Twin Consortiumが2020年12月に公表した定義は、現実世界の実体とプロセスの仮想的な表現であって、指定された頻度と忠実度で同期されているもの、という趣旨のものです。ここでも同期が定義の中心に入っており、しかも頻度と忠実度を指定するという点が明示されています。本章で述べた「意思決定の周期に合わせて同期の粒度を決める」という設計の話は、定義そのものに含まれている論点だということになります。

学術側からは、全米科学・工学・医学アカデミーが2024年に公表した報告書「Foundational Research Gaps and Future Directions for Digital Twins」があります。この報告書はデジタルツインを、計算モデルと物理的な対象を結びつけ、条件の変化に応じて双方向のデータの流れで動的に更新される仕組みと定義しています。そのうえで、デジタルツインは従来のシミュレーションとモデリングを超えるものだと位置づけています。本章で3段階に分けて説明したとおり、双方向であることが分かれ目だという整理は、この報告書の立場と一致します。

同じ報告書は、デジタルツインが広く注目を集めている一方で、分野をまたいだ合意がないために、何が事実で何が願望なのかを切り分けるのが難しい、とも述べています。導入を検討する立場からは、この率直な指摘のほうが実用的かもしれません。

市場規模の数字については、注意が必要です。調査会社によって数字が大きく食い違うためです。次の表は、公表されている3社の推計を並べたものです。

調査会社基準年の市場規模予測年平均成長率
MarketsandMarkets211.4億ドル(2025年)1,498.1億ドル(2030年)47.9%(2025年から2030年)
Precedence Research275.3億ドル(2025年)5,720.3億ドル(2035年)35.44%(2026年から2035年)
Grand View Research358億ドル(2025年)3,285億ドル(2033年)31.1%(2026年から2033年)

同じ2025年の市場規模が211億ドルから358億ドルまで、1.7倍の開きがあります。年平均成長率も31%から48%まで割れています。これは調査会社の質の問題というより、何をデジタルツインとして数えるかの定義が揃っていないことの反映と考えるのが自然です。3Dの可視化ツールを含めるか、センサーの基盤を含めるか、モデルの開発サービスを含めるかで、母数は大きく変わります。

したがって、社内の投資判断でこの種の数字を根拠に使うのは勧められません。市場が伸びていること自体は複数の推計が一致して示していますが、金額の水準を1つの数字として引用すると、根拠として耐えません。判断の根拠は、第16章で述べるとおり、自社のどの意思決定にいくらの誤差があり、それを減らすことにどれだけの価値があるかという計算に置くほうが確かです。

国内では、国土交通省のProject PLATEAUが、全国の都市を対象としたデジタルツインの整備を進めています。3D都市モデルが公開されており、ユースケースにはシミュレーションを用いたものが複数含まれています。自社で一から作る前に、公開されている基盤で試せる範囲を確認しておくと、初期の検証の費用を抑えられます。

この章のまとめ

デジタルツインは、それ自体が目的になる技術ではありません。シミュレーションを一度きりの調査から日常の道具に変えるための形です。中身は、これまでの章で扱ってきた4つの型のいずれか、あるいはその組み合わせであり、そこに同期と継続運用という性質が加わったものだと理解すれば、何を準備すればよいかが具体的に見えてきます。

実務上の判断としては、3段階のどこを目指すかを、意思決定の周期から逆算して決めることになります。多くの業務では、片方向のシャドウの段階で目的を果たせます。双方向にするかどうかは、技術の問題ではなく、誤りが起きたときの影響を引き受けられるかという設計と責任の問題です。名乗りにこだわらず、実態として何がつながっているかを把握することが、次の投資の判断を正確にします。

そして、どの段階を目指すにしても、出発点は1つの工程で使えるモデルを作ることです。データ基盤から始めるのではなく、モデルから始める。この順序が、本章を通じて繰り返し出てきた原則でした。では、その最初のモデルをどうやってプロジェクトとして立ち上げ、どう運用に引き渡すのか。第16章では、導入の順序と、そこで実際に起きる問題を扱います。

この章を深めたい方への参考書籍

デジタルツイン活用事例集(野村淳一 ほか、エヌ・ティー・エス)
製品開発から都市開発、サービスまで、デジタルツインの適用例を集めた本です。自社に近い領域で何が試みられているかを探すときの一次情報源になります。

データ同化流体科学(大林茂・三坂孝志・加藤博司・菊地亮太、共立出版)
観測データを計算モデルに取り込んで現象を再現するデータ同化を扱う本です。モデルを現実と同期させるとは技術的に何をすることなのかが、具体的に分かります。

第16章 プロジェクトとして進める、導入の順序と落とし穴

ここまでの15章は、モデルの型、乱数の扱い、入力の分布、必要な反復回数、検証の手続き、実験の設計、そして現場との接続といった技術の話でした。本章は技術の話をしません。扱うのは進め方です。同じ技術を使っても、進め方を間違えたプロジェクトは成果が出ないまま終わり、進め方が整っていたプロジェクトは粗いモデルでも意思決定に効きます。この差は技術力ではなく、順序と体制と合意の取り方で決まります。

シミュレーションのプロジェクトが止まる原因には、はっきりした型があります。作るものが決まらない、現場と話がずれている、データが出てこない、作った人がいなくなる。どれも高度な問題ではありません。着手の順序を変えるだけで避けられるものがほとんどです。逆に言えば、順序を間違えると、どれだけ優秀な人が実装しても取り返せません。

本章は、経営層やDX推進の担当者がそのまま計画書に落とせる粒度で書きます。段階の一覧、体制の要件、工数がどこに食われるか、よくある失敗の型、運用に残すために必要なもの、そして広げ方と投資判断の考え方までを順に扱います。

最初に決めるのは、答える問いを1つに絞ること

「工場をモデル化する」は、プロジェクトではありません。作業の名前ではありますが、完了の判定ができないため、計画として成立していません。工場のどこまでを含めるのか、どの精度で作るのか、どうなったら終わりなのか、何も決まっていないからです。この状態で着手すると、作れば作るほど「あれも入っていない」「これも再現できていない」という指摘が増え、際限なく詳細化して期限を越えます。

プロジェクトとして成立するのは、たとえば「第2ラインの増設によって、繁忙期のリードタイムがどこまで縮むか」という形です。これは問いです。答えが出れば終わりだと判定できます。そして問いが決まった瞬間に、それ以外のことが自動的に決まっていきます。繁忙期を対象にすると決めたので、閑散期のデータは主役ではなくなります。リードタイムを見ると決めたので、品質不良の再現は今回の範囲から外せます。第2ラインの有無を比べると決めたので、比較すべき条件が2つに定まります。

粒度も問いから決まります。リードタイムの差を見たいのであれば、工程単位の所要時間が入っていれば足ります。1台ごとの機械の細かな動作を再現する必要はありません。逆に、特定の設備のボトルネックを特定したいのであれば、その設備だけは細かく、周辺は粗くという配分になります。粒度を先に決めようとすると必ず迷いますが、問いを先に決めれば粒度は結果として決まります。この順序が逆になっているプロジェクトが非常に多いように思います。

データ要件も同じです。問いが決まっていないと「とりあえず全部のデータをください」という依頼になり、情報システム部門で止まります。問いが決まっていれば「繁忙期3か月分の、工程Aから工程Eまでの開始時刻と終了時刻」という依頼になります。相手が用意できる粒度の依頼になり、話が進みます。データが出てこないプロジェクトの多くは、データがないのではなく、依頼が具体的でないために優先度が上がらないだけです。

問いを1つに絞ることに抵抗が出る場面もあります。関係者が複数いると、それぞれが知りたいことを持ち寄って、問いが5つ6つに膨らみます。このとき、すべてに答えようとすると1つも答えられません。順番をつけて、最初の1つを決めます。残りは範囲外として明記し、次の段階で扱うと約束します。捨てるのではなく後に回すという整理にすると、合意が得やすくなります。

完了条件を先に書く

問いが決まったら、次に完了条件を書きます。書く内容は3つです。どの指標を、どの精度で、いつまでに出すか。この3つが揃った文になっていれば完了条件として機能します。たとえば「平均リードタイムを、95%信頼区間の幅0.5日以内で、3か月後の投資判断会議までに出す」という形です。

指標を書く意味は、見るものを固定することにあります。リードタイムと書いたのであれば、稼働率が改善したかどうかは今回の判定材料ではありません。指標を固定しないと、結果が出たあとに「稼働率も見たい」「仕掛在庫も見たい」と追加され、そのたびに実験をやり直すことになります。追加の指標を出すこと自体は構いませんが、それは完了条件の変更であり、期限と工数の見直しを伴うという扱いにします。

精度を書く意味は、計算量を決めることにあります。第9章で扱ったとおり、シミュレーションの出力は乱数によってぶれるので、1回の実行で出た数字には幅があります。どこまで狭い幅で答えるかを先に決めておくと、必要な反復回数が逆算できます。逆に精度を書かないと、何回まわせば十分かの判断基準がなくなり、「もう少し回してみましょう」が延々と続きます。精度は実務の意思決定に必要な水準から決めます。0.5日の差で判断が変わらないのであれば、0.1日の精度を追う必要はありません。

期限を書く意味は、意思決定の日に合わせることにあります。シミュレーションは意思決定のための道具なので、意思決定の日を過ぎて出た数字には価値がありません。投資判断会議が3か月後にあるのなら、その1か月前には結果が出ていて、解釈と提言をまとめる時間が要ります。期限から逆算して、実装や実験に使える期間が決まり、そこから作れるモデルの規模が決まります。期限を後回しにすると、モデルの規模が先に決まってしまい、期限が後ろにずれます。

完了条件は、着手前に関係者の署名がある形で残します。口頭の合意では、結果が出たあとに認識がずれます。文書にしておくと、途中で追加要望が出たときに「それは完了条件に入っていないので、別の段階として扱いましょう」という会話ができます。この会話ができるかどうかが、期限を守れるかどうかを分けます。

標準的な進め方、9つの段階

進め方は、次の9段階に整理できます。段階を飛ばさないこと、そして前の段階の成果物が次の段階の入力になっていることが要点です。飛ばして先に進んでも、必ず後で戻ることになり、戻る分だけ時間を失います。

段階やることこの段階の成果物飛ばすとどうなるか
1. 問いの定義と範囲の確定答える問いを1つに絞り、どこからどこまでをモデルに含めるかを決める問いと完了条件の文書、範囲の境界際限なく詳細化し、完了の判定ができない
2. 現状の業務フローの把握現場を観察し、担当者に聞き、実際の運用の順序と例外を書き出す業務フローの記述、例外処理の一覧手順書どおりの理想の工程をモデル化してしまう
3. 概念モデルの作成と合意数値を入れる前に、構造の絵で関係者と合意する概念モデル図、含めるもの・含めないもの・仮定の一覧後工程がすべて無駄になる可能性がある
4. データの収集と入力モデリング実績データを集め、分布を当てはめる。第8章の手順入力分布の一覧、データの出所と期間の記録平均値だけのモデルになり、ばらつきの影響が消える
5. 実装小さく作って動かす。まず単純な構成で通し、段階的に足す動くモデル、実装の設計メモ一度に全部作り、どこが原因で動かないか分からなくなる
6. 検証と妥当性確認実装が意図どおりか、現実を十分写しているかを確かめる。第10章の手順検証の記録、適用範囲の宣言、既知の限界正しく計算した誤った数字が意思決定に流れる
7. 実験の設計と実行比べる条件を設計し、必要な反復回数をまわす。第9章と第11章の手順実験計画、条件ごとの推定値と信頼区間1回の実行結果や場当たりの条件で結論を出す
8. 結果の解釈と提言数字を意思決定の言葉に翻訳し、前提と限界を添えて示す報告書、判断の選択肢と根拠数表だけが残り、誰も判断に使えない
9. 運用への引き継ぎ更新手順・台帳・動かせる人を整える運用手順書、モデル台帳の登録、担当者2名以上作った人が離れた時点でモデルが死ぬ

この表で特に強調したいのが第2段階です。現場の観察とヒアリングは、忙しさを理由に飛ばされやすい工程です。既存の手順書や工程表があると、それを読めば足りるように見えます。しかし手順書に書かれているのは理想の流れであり、実際の現場には必ず書かれていない運用があります。特定の条件では順番を入れ替える、繁忙期だけ別の担当者が応援に入る、機械の調子が悪い日は手前で在庫を積む。こうした運用こそが、リードタイムやボトルネックの位置を決めていることが少なくありません。

第5段階の「小さく作る」も、方針として明記しておく価値があります。最初から完成形を目指すと、動くまでに時間がかかり、動かないときの原因の切り分けが困難になります。まず単純な構成、たとえば工程を3つに絞って通し、動くことを確かめてから要素を足していきます。段階ごとに検証を挟めるので、壊れた箇所がその場で分かります。

プロジェクトの9段階を左から右へ矢印で並べたフロー図

概念モデルの合意が、後工程のすべてを決める

9つの段階のうち、最も重要なのは第3段階の概念モデルの合意です。ここで現場と食い違ったまま進むと、後工程がすべて無駄になります。データを集めるのも、実装するのも、実験するのも、間違った構造の上での作業になるためです。しかもこの食い違いは、実装が終わって結果を見せた段階で初めて表面化することが多く、そのときには最も手戻りが大きくなっています。

概念モデルとは、数値の入っていない構造の絵です。何が流れ、どこで待ち、どの資源を使い、どの順序で処理されるか。ボックスと矢印で書けます。この段階では分布も所要時間も出てきません。構造だけを見せて、現場の担当者に「これで合っているか」と確認します。数値が入っていないほうが、構造そのものに注意を向けてもらいやすくなります。

合意すべき内容は3点に整理できます。第一に、モデルに含めるもの。どの工程、どの資源、どの種類のエンティティを表現するか。第二に、含めないもの。今回は表現しない要素を明示的に列挙します。品質不良は扱わない、外注工程は所要時間の分布だけで表す、設備の予防保全は考慮しない、といった形です。第三に、仮定。待ち行列は先着順とする、作業者の熟練度差は考慮しない、昼休みは全員一斉とする、といった前提を書き出します。

この3点を書いた文書が、第10章で扱った適用範囲の宣言の土台になります。どの条件の範囲で、どの指標について答えられるかを先に宣言しておくと、後から「この条件でも試してほしい」という依頼が来たときに、答えられる依頼と答えられない依頼を切り分けられます。宣言がないモデルは、範囲外の質問に対しても数字を返してしまい、その数字が独り歩きします。

「含めないもの」を書く作業は、書き手にとって気が進まない作業です。できないことを列挙するように見えるためです。しかし実務では、この列挙が最も強く効きます。含めない理由を添えて示すと、現場から「それなら今回の目的には足りている」という納得が得られるか、あるいは「それは外せない」という指摘が出ます。後者が出た場合、その場で範囲を見直せます。実装が終わったあとに同じ指摘が出るのに比べれば、失う時間は桁違いに小さくなります。

合意の取り方にも要点があります。会議で図を映して「異論ありませんか」と聞くだけでは、合意になりません。参加者は図を初めて見た場では違和感を言語化できないことが多いためです。図を事前に配り、現場の担当者が自分の担当工程を指でたどれる形にして、その工程で実際に起きることを口頭で説明してもらいます。説明の途中で図に描かれていない動きが出てきたら、それが漏れです。この確認の仕方だと、漏れが自然に表に出ます。

4者が揃わないプロジェクトは止まる

体制には、役割の異なる4者が要ります。問いのオーナー、モデルを作る人、現場の実務を知る人、データを出せる人です。この4者のうち1つでも欠けると、どこかの段階で必ず止まります。人数の問題ではなく、役割の問題です。1人が2役を兼ねることはありますが、役割そのものが空席だと進みません。

役割担うこと欠けると起きること関与が要る段階
問いのオーナー意思決定者。問いを決め、完了条件を承認し、結果を判断に使う作ったものが誰の判断にも使われず、報告書で終わる1、3、8
モデルを作る人概念モデルの設計、実装、検証、実験の設計と実行そもそも着手できない3から7
現場の実務を知る人実際の運用の順序と例外を伝え、概念モデルの妥当性を判定する手順書どおりの理想のモデルができ、結果が現場に否定される2、3、6
データを出せる人実績データの所在を知り、抽出して渡す。データの意味を説明する入力が仮定だけになり、結論の根拠が弱くなる4、6

とくに注意が要るのが、現場の実務を知る人です。この役割を後から呼ぶと、ほぼ確実に作り直しになります。実装が終わってから現場に見せて「うちはそういう流れではない」と言われた場合、直すのは実装の一部ではなく構造そのものです。第2段階と第3段階に確実に入ってもらう前提で日程を組みます。現場は本業が忙しいので、初回の観察と概念モデルの確認という2回の時間を、着手前に押さえておく進め方が現実的です。

問いのオーナーの関与も軽視されがちです。担当者レベルで進めて、最後に意思決定者へ報告する形だと、報告の場で「知りたかったのはそこではない」という反応が出ます。問いの定義と概念モデルの合意の2つの場面には、意思決定者本人に入ってもらいます。この2回だけで足りることが多いので、時間の負担は大きくありません。

工数の配分については、計画段階で経営に理解してもらうべき点が1つあります。実装、つまりコードを書く時間は、プロジェクト全体の一部にすぎないということです。時間を食うのは、データの整備と、検証と妥当性確認です。データは形式が揃っていないことが普通で、欠損や重複や単位の不統一を直す作業が発生します。検証と妥当性確認は、現場との往復が入るため、待ち時間も含めて日程が伸びます。

具体的な比率は案件によって大きく変わるため、一律の数値は挙げません。データが整備済みの環境と、紙の記録から起こす環境では、まったく違う配分になります。計画では、実装以外の工程に十分な期間を置き、進捗をコードの完成度で測らないという方針を共有しておきます。実装が動いた時点を「7割終わった」と受け取ると、その後の検証とデータ修正の期間が計画外の遅れに見えてしまいます。

失敗の型を先に知っておく

シミュレーションのプロジェクトが失敗する形には、繰り返し現れる型があります。型として知っておくと、進行中に「今この型に入りかけている」と気づけます。以下に主要なものを挙げます。

失敗の型どう現れるか根本にある原因避け方
目的がモデルを作ることになる再現度の議論ばかりが続き、どの判断に使うかが話題にならない問いが決まっていない問いと完了条件を着手前に文書で固定する
精度を追って粒度を上げ続ける指摘のたびに要素を追加し、期限が繰り返し延びるどの精度で十分かの基準がない完了条件に精度を書き、それを超える要求は次段階に回す
現場を巻き込まない技術的には正しいのに、現場が結果を受け入れない概念モデルの合意を経ていない第2、第3段階に現場担当者を必ず入れる
1回の実行結果で結論を出す案Aが案Bより良いと報告したが、再実行すると逆転する乱数によるぶれを考慮していない。第9章反復し、信頼区間で比べる
入力を平均値で埋める実績より待ち時間や在庫が小さく出て、現場感覚と合わないばらつきを落としている。第8章分布を当てはめるか、経験分布を使う
検証していない条件に外挿する合わせ込んだ範囲の外の条件で数字を出し、判断を誤る適用範囲を宣言していない。第10章範囲外の問いには答えないと明記する
作った人が異動して動かせなくなる半年後に同じ検討をしようとして、誰も実行できない属人化。引き継ぎの設計がない台帳と手順書を整え、動かせる人を2名以上置く
ツールの選定から入る製品を決めたあとで、それでは答えられない問いだと分かる道具を問いより先に選んでいる問いと概念モデルを固めてから道具を選ぶ

最初の型、つまり目的がモデルを作ること自体になってしまう現象は、最も頻度が高く、最も気づきにくいものです。進捗会議で議論されているのが「現場の動きをどこまで再現できているか」ばかりになったら、この型に入っています。再現度は手段であって目的ではありません。会議の議題に「この結果でどの判断ができるか」を常に含めておくと、軌道が戻ります。

最後の型、ツールの選定から入る失敗も、組織の意思決定の順序として起こりやすいものです。予算を取る都合で製品の購入が先に決まり、その製品でできる範囲に問いを合わせる形になります。第14章で扱ったとおり、道具にはそれぞれ得意な問いの型があるので、問いが決まる前に道具を決めると、選択肢を自分で狭めることになります。予算の制約でやむを得ない場合でも、せめて概念モデルの粗い形だけは先に描いておくと、選定の判断材料になります。

半年後にも動かすために残す6点

結果を出して報告した時点で終わるプロジェクトは、投資としては半分しか回収できていません。同じ問いは形を変えて再び来ますし、条件を変えた再検討の依頼も来ます。そのときに動かせないモデルは、資産になりません。運用の引き継ぎを第9段階として計画に入れておく理由がここにあります。

残すべきものは6点あります。第一に、入力データの更新手順です。どのシステムのどのテーブルから、どの期間を、どういう手順で抽出し、どう整形してモデルに入れるか。実行できる形の手順書にします。抽出用のスクリプトがあるなら、その置き場所と実行方法も含めます。この手順がないと、半年後に同じデータを集める作業が最初からやり直しになります。

第二に、乱数の種の記録です。どの結果がどの種で得られたかを残しておくと、後から同じ結果を再現できます。結果に疑問が出たときの確認でも、誤りの調査でも、再現できるかどうかが調べられる範囲を決めます。第11章で扱った共通乱数法を使った比較では、案の間で種をどう揃えたかも記録に含めます。

第三に、仮定の一覧です。概念モデルの合意で書き出した仮定を、そのまま運用文書に引き継ぎます。半年後にモデルを開いた人が最初に読むのはこの一覧です。仮定が分からないまま条件だけ変えて実行すると、範囲外の使い方に気づけません。

第四に、検証の記録です。どの手法で何を確認し、何が分かったか。理論値と突き合わせたのであれば、条件と理論値と推定値と信頼区間を残します。実施しなかった項目についても、実施しなかったと書いておきます。空欄と「未実施」は意味が違い、後者だけが引き継げる情報になります。

第五に、モデル台帳です。組織の中にモデルが増えてくると、どこに何があるか分からなくなります。台帳には、モデルの名前、答える問い、適用範囲、作成日と最終更新日、現在の担当者、ファイルの置き場所、関連文書の場所を1行にまとめて登録します。数が少ないうちは表計算ソフトの1枚のシートで足ります。台帳がないと、似たモデルが別々の部署で二重に作られたり、古いモデルが更新されないまま使われたりします。

第六に、動かせる人が2人以上いることです。これは文書ではなく体制の条件です。1人しか動かせないモデルは、その人の異動や退職とともに使えなくなります。2人目は必ずしも作れる人である必要はなく、手順書に沿って実行し、結果を読める人であれば足ります。実際に手順書だけを見て2人目に実行してもらうと、手順書の不備がその場で分かるという副次的な効果もあります。

この6点が揃っていれば、半年後にも動きます。逆に、どれか1つでも欠けていると、動かすための調査から始めることになり、その調査の工数が「作り直したほうが早い」という判断につながります。引き継ぎの整備にかかる時間は、作り直しの時間よりはるかに短く済みます。

運用に残す6点を表す一覧図

1つの問いから広げる、そして投資判断の考え方

導入の広げ方には、順序があります。最初から全社の基盤を作ろうとすると、対象が大きすぎて成果が出るまでに時間がかかり、その間に推進の意欲が続きません。1つの問いで成果を出すところから始めます。1つの問いに答えて、その答えが実際の判断に使われたという実績を作ります。この実績が、次の段階の予算と協力を引き出します。

次の段階は、同じモデルを別の問いに使うことです。第2ラインの増設効果を見るために作ったモデルは、多くの場合、シフト構成の変更や、ロットサイズの見直しの検討にも使えます。適用範囲の宣言を確認して、範囲内であればそのまま使い、範囲外であれば必要な部分だけ拡張します。ここで得られる効果は大きく、1つ目のモデルを作る費用は変わらないのに、そこから得られる答えの数が増えます。

その次が、隣接する工程への拡張です。組立工程で成果が出たなら、その前後の受入や出荷に範囲を広げます。すでに概念モデルの作り方と、データを出す手順と、検証の進め方が組織に定着しているので、2件目以降は1件目より短い期間で進みます。最後に、定期的に回す運用に乗せます。月次で需要見通しを入れ替えて実行し、翌月の要員計画の材料にする、といった形です。

第15章で扱ったデジタルツインは、この延長線上にあります。現場のデータと継続的に同期し、常に最新の状態でモデルが動いている形は、定期実行の運用がさらに進んだ姿です。いきなりそこから始めるものではありません。同期の仕組みを先に作っても、そのモデルが答える問いが定まっていなければ、データが流れ込むだけの装置になります。1つの問いで成果を出す段階を踏まずに最終形へ飛ぼうとすることが、この分野での典型的な失敗の1つです。

最後に、投資判断の考え方を述べます。判断の枠組み自体は単純で、モデルを作るためにかかる費用と、判断を間違えたときに生じる損失を比べます。シミュレーションは、実物で試せないことを試すための道具なので、実物で試した場合の費用や、試さずに決めて外した場合の損失が比較対象になります。設備を導入してから能力不足が分かった場合の損失、人員配置を変えてから待ち時間が悪化した場合の影響、在庫方策を変えてから欠品が増えた場合の機会損失といったものです。

この構造から導かれるのは、判断の金額規模が大きいほど、モデルの費用対効果は良くなるということです。モデルを作る費用は、対象の規模に比例して増えるわけではありません。同じ手間で作ったモデルが、小さな判断に使われるか大きな判断に使われるかで、得られる価値が変わります。したがって、最初の1件を選ぶときは、判断の規模が大きく、かつ実物で試すことが難しい領域を選ぶのが合理的です。設備投資、拠点の新設、大きな運用ルールの変更といった領域がこれにあたります。

逆に、判断の規模が小さく、実際に試して失敗しても戻せる領域では、モデルを作るより実際に試したほうが早いこともあります。シミュレーションを使うべきかどうかの判断にも、この比較が使えます。何にでも使う道具ではなく、試すことの費用が高い場面のための道具だと位置づけると、導入の対象を選びやすくなります。

この章を深めたい方への参考書籍

システム思考がモノ・コトづくりを変える(稗方和夫・髙橋裕、日経BP)
システム思考をものづくりや事業の設計に使う視点でまとめた本です。モデルを作る話ではなく、組織で使う話として読めます。

学習する組織(ピーター・M・センゲ 著、枝廣淳子・小田理一郎・中小路佳代子 訳、英治出版)
組織が構造をどう学ぶかを扱った本です。モデルを作っても使われないという本章の失敗が、なぜ起きるのかを別の角度から考えられます。

終章 試してから決めるという仕事の作法

ここまで、モンテカルロ法から離散イベントシミュレーション、エージェントベースモデリング、システムダイナミクスまでの4つの型と、それを実務で使うための工程を順に見てきました。最後に、技術の話から少し離れて、この道具が組織にもたらすものを整理します。

意思決定の作法が変わる

シミュレーションを導入して最も変わるのは、計算の精度ではなく議論の形です。設備を1台増やすかどうかを決める会議で、これまでは「増やしたほうがいい」「いや現状で回る」という主張がぶつかり、最後は声の大きさか役職で決まっていたものが、「増やした場合のリードタイムの分布はこう、増やさない場合はこう」という比較に置き換わります。

この置き換えの本質は、正解が出ることではありません。前提が表に出ることです。意見が割れたときに、割れているのは結論ではなく前提であることが多い。到着の見込みが違うのか、作業時間の見立てが違うのか、故障の頻度の想定が違うのか。モデルがあれば、その食い違いを入力の数値として特定でき、そこだけを議論できます。第10章で「適用範囲を宣言する」ことを強調したのは、この効果のためでもあります。

もうひとつ変わるのが、失敗の扱いです。実物で試せば失敗はコストですが、モデルの中では失敗は情報です。うまくいかない案を10通り試して捨てられることが、モデルを持つことの実質的な価値になります。改善活動が「やってみて様子を見る」から「試してから手を付ける」に変わると、現場に手を入れる回数そのものが減り、混乱も減ります。

最適化との使い分けを、もう一度

本コラムを通じて置いてきた軸を、あらためて確認します。最適化は「どの案が最良か」を数式の中で求める技術で、数式に書けたものについては最良を保証します。シミュレーションは「この案だと何が起きるか」を再現する技術で、最良は保証しませんが、数式に収まらない現実を持ち込めます。

どちらか一方を選ぶ話ではありません。第12章で見たように、両者は組み合わさります。最適化が出した計画をシミュレーションで検証する。シミュレーションを評価関数として最適化の探索をまわす。粗い最適化で候補を絞り、シミュレーションで精査する。実務で強いのは、この行き来ができる体制です。

判断の目安として、次の問いが役に立ちます。案を数え上げられるか。案の良し悪しを閉じた式で測れるか。両方とも「はい」なら最適化の領分です。二番目が「いいえ」なら、評価の部分にシミュレーションが要ります。一番目も「いいえ」なら、探索と評価を組み合わせることになります。

小さく始めて、運用に残す

最後に、進め方について繰り返しておきます。シミュレーションの導入で失敗する典型は、精度を上げようとして粒度を上げ続け、完成しないまま予算が尽きる形です。第10章と第16章で述べたとおり、答えるべき問いを1つに絞り、その問いに必要な粒度でだけ作る。作ったら早く現場に見せて、違和感を吸い上げる。

そして、作ったモデルを一度きりの成果物で終わらせないことです。入力データの更新手順、乱数の種の記録、仮定の一覧、検証の記録。この4つが揃っていれば、モデルは半年後にも動かせます。揃っていなければ、作った人がいなくなった時点で捨てられます。第15章で扱ったデジタルツインは、この「運用に残す」を突き詰めた先にある形であり、いきなりそこから始めるものではありません。

予測が「何が起きそうか」を、最適化が「どれが最良か」を担うなら、シミュレーションは「この案だと何が起きるか」を担います。この3つが揃って初めて、データにもとづく意思決定は一巡します。どれか1つが欠けたまま議論している場面は、実務ではまだ多いように思います。本コラムが、その欠けている部分に気づく手がかりになれば幸いです。

Anagraftでは、AIプロジェクトの構想・課題設計から、データ分析・機械学習モデルの開発、AI人材の育成まで一貫したご支援を行っています。会社概要・ご支援内容の詳細は、以下の資料からご覧いただけます。

Anagraft 会社概要資料(ダウンロードページへ)

本コラムで紹介した書籍一覧

各章の末尾で挙げた書籍をまとめました。書名・著者名・出版社は、国立国会図書館サーチと各出版社の公式ページで確認しています。

OR入門(宮川公男、丸善出版)
オペレーションズ・リサーチの全体像を1冊で見渡せる入門書です。シミュレーションが数理最適化や待ち行列とどう並ぶ道具なのかを、分野の地図として確かめたいときの出発点になります。
本コラムでは第1章、第12章で挙げました。

シミュレーション(白鳥則郎 監修、佐藤文明・齊藤稔・石原進・渡邊尚 著、共立出版)
モデル化から乱数生成、待ち行列モデル、微分方程式、セルオートマトン、マルチエージェントまでを一続きで扱う教科書です。本コラムで型ごとに分けて説明した内容を、体系立てて読み直せます。
本コラムでは第2章、第14章で挙げました。

シミュレーション辞典(日本シミュレーション学会 編、コロナ社)
学会が編んだ辞典です。手法名や用語の定義を確かめたいとき、分野をまたいで一度に引ける参照先になります。
本コラムでは第2章で挙げました。

モンテカルロ法ハンドブック(Dirk P. Kroese・Thomas Taimre・Zdravko I. Botev 著、伏見正則・逆瀬川浩孝 監訳、朝倉書店)
モンテカルロ法を体系的にまとめた一冊です。本章で扱った試行と推定の背景を、数理の側から確かめたいときに向きます。
本コラムでは第3章、第11章で挙げました。

ゼロからできるMCMC(花田政範・松浦壮、講談社)
乱数を使って分布を扱う考え方を、手を動かしながら学べる本です。本章のモンテカルロ法から一歩進んだ計算手法に触れたい方に向きます。
本コラムでは第3章で挙げました。

形式的モデル化(平石邦彦、森北出版)
離散事象システムのモデル化と解析を正面から扱う本です。本章で概略を示した仕組みを、形式的な定義から積み上げて理解したい方に向きます。
本コラムでは第4章で挙げました。

待ち行列理論の基礎と応用(川島幸之助 監修、塩田茂雄・河西憲一・豊泉洋・会田雅樹 著、共立出版)
待ち行列理論の基礎から、コールセンタ設計や通信網の性能評価といった応用までを扱います。本章で式の形だけ示した部分を、導出から追いたい方に向きます。
本コラムでは第5章、第13章で挙げました。

わかりやすい待ち行列システム(高橋敬隆 ほか、電子情報通信学会 編、電子情報通信学会(コロナ社発売))
待ち行列を理論と実践の両側から説明した本です。式が現場のどの数字に対応するのかを結びつけたいときに向きます。
本コラムでは第5章で挙げました。

人工社会構築指南 改訂新版(山影進、書籍工房早山)
エージェントベースのモデルを実際に組み立てる手順を扱う本です。個の行動ルールをどう書くかという、本章でいちばん難しい部分に踏み込めます。
本コラムでは第6章で挙げました。

社会シミュレーションの技法(Nigel Gilbert・Klaus G. Troitzsch 著、井庭崇・岩村拓哉・高部陽平 訳、日本評論社)
社会現象をシミュレーションで扱うための技法を整理した本です。個から全体が立ち上がる現象を、研究の系譜として捉え直せます。
本コラムでは第6章で挙げました。

シミュレーションによるシステムダイナミックス入門(土金達男、東京電機大学出版局)
ストックとフローでモデルを組む手順を、実際に動かしながら学べる本です。本章の在庫の例を、自分の業務の構造に置き換えるときの足がかりになります。
本コラムでは第7章で挙げました。

システム思考をはじめてみよう(ドネラ・H・メドウズ 著、枝廣淳子 訳、英治出版)
フィードバックループという見方を、平易な言葉で説明した薄い本です。数式に入る前に、構造で考えるとはどういうことかを掴めます。
本コラムでは第7章で挙げました。

Pythonコンピュータシミュレーション入門(橋本洋志・牧野浩二、オーム社)
人文・自然・社会科学の数理モデルをPythonで動かす入門書です。分布や乱数の扱いが結果をどう変えるかを、手を動かして確かめられます。
本コラムでは第8章で挙げました。

Simulation Modeling and Analysis 第6版(Averill M. Law、McGraw-Hill)
シミュレーションの標準的な参考書です。妥当なモデルの作り方、出力データの分析、代替案の比較、分散減少法、実験計画と最適化がそれぞれ独立した章として扱われており、本コラムの後半の各章に対応します。
本コラムでは第9章、第10章で挙げました。

マルチエージェントによる金融市場のシミュレーション(高安美佐子・和泉潔・山田健太・水田孝信、コロナ社)
金融市場をエージェントベースで扱う研究をまとめた本です。個々の参加者の行動から市場全体の挙動が生まれる過程を、具体例として追えます。
本コラムでは第13章で挙げました。

Pythonによるシミュレーションモデリング(Giuseppe Ciaburro 著、黒川利明 訳、朝倉書店)
Pythonでシミュレーションを組む手順をまとめた本です。本章で紹介した道具立てを、自分の環境で動かしてみるときの手引きになります。
本コラムでは第14章で挙げました。

デジタルツイン活用事例集(野村淳一 ほか、エヌ・ティー・エス)
製品開発から都市開発、サービスまで、デジタルツインの適用例を集めた本です。自社に近い領域で何が試みられているかを探すときの一次情報源になります。
本コラムでは第15章で挙げました。

データ同化流体科学(大林茂・三坂孝志・加藤博司・菊地亮太、共立出版)
観測データを計算モデルに取り込んで現象を再現するデータ同化を扱う本です。モデルを現実と同期させるとは技術的に何をすることなのかが、具体的に分かります。
本コラムでは第15章で挙げました。

システム思考がモノ・コトづくりを変える(稗方和夫・髙橋裕、日経BP)
システム思考をものづくりや事業の設計に使う視点でまとめた本です。モデルを作る話ではなく、組織で使う話として読めます。
本コラムでは第16章で挙げました。

学習する組織(ピーター・M・センゲ 著、枝廣淳子・小田理一郎・中小路佳代子 訳、英治出版)
組織が構造をどう学ぶかを扱った本です。モデルを作っても使われないという本章の失敗が、なぜ起きるのかを別の角度から考えられます。
本コラムでは第16章で挙げました。

参考情報・出典

本コラムで数値や定義を引用した箇所の出典です。いずれも2026年8月に内容を確認しています。

規格・公的資料

  • ISO 23247-1:2021, Automation systems and integration, Digital twin framework for manufacturing, Part 1: Overview and general principles
  • ISO 23247-2:2021, Part 2: Reference architecture / ISO 23247-3:2021, Part 3: Digital representation of manufacturing elements / ISO 23247-4:2021, Part 4: Information exchange / ISO 23247-5:2026, Part 5: Digital thread for digital twin / ISO 23247-6:2026, Part 6: Digital twin composition
  • National Academies of Sciences, Engineering, and Medicine, Foundational Research Gaps and Future Directions for Digital Twins, 2024
    https://www.ncbi.nlm.nih.gov/books/NBK605515/
  • 国土交通省 Project PLATEAU
    https://www.mlit.go.jp/plateau/

学術文献

  • Little, J. D. C.「A Proof for the Queuing Formula: L = λW」Operations Research、第9巻第3号、1961年、383-387頁
  • Kingman, J. F. C.「The single server queue in heavy traffic」Mathematical Proceedings of the Cambridge Philosophical Society、第57巻第4号、1961年、902-904頁
  • Nance, R. E.「The Time and State Relationships in Simulation Modeling」Communications of the ACM、第24巻第4号、1981年、173-179頁
  • Schmeiser, B.「Batch Size Effects in the Analysis of Simulation Output」Operations Research、第30巻第3号、1982年、556-568頁
  • Sargent, R. G.「Verification and Validation of Simulation Models」Proceedings of the 2010 Winter Simulation Conference、2010年、166-183頁
    https://www.informs-sim.org/wsc10papers/016.pdf
  • Currie, C. S. M. および Cheng, R. C. H.「A Practical Introduction to Analysis of Simulation Output Data」Proceedings of the 2013 Winter Simulation Conference、2013年、328-341頁
    https://informs-sim.org/wsc13papers/includes/files/028.pdf
  • Hoad, K.、Robinson, S. および Davies, R.「Automated selection of the number of replications for a discrete-event simulation」Journal of the Operational Research Society、第61巻、2010年、1389-1403頁
  • Adan, I. および Resing, J.「Queueing Theory」Eindhoven University of Technology、2001年
    https://people.math.carleton.ca/~zhao/research/QueueingTheory_AdanResing.pdf
  • Law, A. M.「Simulation Modeling and Analysis」第6版、McGraw-Hill、2024年
  • Banks, J.、Carson, J. S.、Nelson, B. L. および Nicol, D. M.「Discrete-Event System Simulation」第5版、Prentice Hall、2010年
  • Grieves, M. および Vickers, J.「Digital Twin: Mitigating Unpredictable, Undesirable Emergent Behavior in Complex Systems」Transdisciplinary Perspectives on Complex Systems 所収、Springer、2017年、85-113頁

公表されている事例

市場規模の推計

ツールの公式サイト

本コラムに掲載したPythonのコードと実行結果は、いずれも実際に実行して得たものです。乱数の種はコード内に明記していますので、同じ環境であれば同じ結果が再現できます。