行列数学で Factorio の品質確率を解く
Factorioの作者は行列数学のコードを書いて工場を計画している。品質ループをマルコフ連鎖として構築し、定常方程式を解き、いくつかのループ戦略の期待生産量を比較し、オンライン計算機を作った。無限級数の反復ではなく厳密解であることがこの手法の新規性だと彼は考えている。
日本語
コピー

Factorioの遊び方はいたって普通だ。行列演算のコードを書いて工場を設計する。
それは後で話すとして、まず TL;DR を。新しく作ったオンライン計算ツールをどうぞ。
Factorioと品質の仕組み入門
Factorioは工場ゲームというジャンルをほぼ確立した。 プレイヤーはキャラクターを操作して資源を採集し、それらを組み合わせて どんどん複雑なアイテムを作る。そしてそのアイテムが、さらに複雑な 製作を支える。Minecraftや他のサバイバルゲームと似ているように聞こえるが、 工場ゲームが違うのは自動化への比重だ。しばらくすると、製作のほとんどは キャラクターの「手作業」ではなく、増え続ける機械が担うようになる。 ベルトコンベアなどの物流手段を組み合わせて、アイテムを機械間や 必要な場所へ運ぶ。プレイヤーキャラクター自体を廃止した工場ゲームもある。
ゲーム内の「テクノロジー」が解放されるにつれ、Factorioは生産量を 高める仕組みを多数用意している。そのひとつがモジュールだ。製作機械には (限られた数の)スロットがあり、種類の異なるモジュールを挿して 性能を変えられる。速度モジュールは機械を速くする代わりに消費電力が増え、 生産性モジュールは同じ原料で生産量を増やす代わりに速度と電力が犠牲になる、 といった具合だ。
2024年リリースのSpace Age拡張では新しい仕組みが追加され、そのひとつが 品質だ。今やすべてのアイテムとレシピに5段階の品質がある: ⚀ ノーマル、⚁ アンコモン、⚂ レア、⚃ エピック、⚄ レジェンダリー。 何が向上するかはアイテム次第で、製作機械が速くなったり、 生産性モジュールの生産性が上がったりする。高品質のアイテムは 同じ品質の原料から直接作れるが、品質を上げる唯一の方法は 新しく追加された品質モジュールだ。

品質モジュール自体にも品質がある。出典: Factorio wiki
モジュールは品質アップグレードの確率に影響する。 1回の製作で、最初の1段階より後の各段階は追加で10%の確率になる。 入力品質ごとに、出力品質の確率表を書ける:

出典: Factorio wiki
たとえばモジュールスロットが4つある機械では、最高品質の確率は24.8%になる:

ノーマルから一足飛びにレジェンダリーへ行ける確率はわずか0.0248%。出典: Factorio wiki
ノーマルから一足飛びにレジェンダリーへ行ける確率はわずか0.0248%。 出典: Factorio wiki
おおむね決定論的なゲームにランダム性を持ち込むのを好まないプレイヤーもいるが、試行回数が十分多ければ確率は割合になる。
確率はバランス調整済みだ。複数の製作工程(各工程で品質ジャンプが起こりうる)を経て高品質アイテムを手に入れようとすれば、望まない低品質アイテムも大量にできてしまう。
倉庫が溢れて工場が止まるのを避けるため、Space Ageはリサイクラーも導入した。どんなアイテムでも破壊し、(通常)原料の25%を返す新しい機械だ。これによりプレイヤーは「アップサイクル」装置を設計できる。品質モジュールを付けた組立とリサイクルを、アイテムが望む品質になるまで循環させるのだ。代償ははるかに多くの原料だ:
出典: Factorioブログ
工場計画ツール
テレビゲームの中には、一部がゲームの外で「遊ばれる」ものがある。Blue Princeは、見たものすべてをプレイヤー自身が細かく記録することを前提にしていて、ゲーム内のメモ帳のようなものは用意していない。工場ゲームは巨大なスプレッドシートを組んで資源計算をするのに向いているが、スプレッドシートでは工場を設計するには足りないと判断した一部のプレイヤーは、専用ツールの開発に膨大な時間を費やし、ゲーム内の数学を大量に再現して生産チェーンを正確にモデル化している。

Factoriolabでの生産チェーンの例
工場ゲームではこれらはすべて任意だ。感覚で遊んで、足りないものを適当に増やしてもいい。
だが私は事前に計画するのが好きだ。各機械は何台必要か? 見込み生産量は? ボトルネックはどこか? 品質リサイクルの循環的な性質が、これを格別に厄介にする。勘でやるにせよ、既存ツールで計算するにせよ。
行列演算
こう考えてみよう:
- 一部の原料(たとえば鉄板)は 品質モジュールを使うかもしれない任意の生産チェーンから来る。 どの原料も各品質段階に落ちる確率がある: ⚀ ノーマル、⚁ アンコモン、⚂ レア、⚃ エピック、⚄ レジェンダリー。
- 各レシピ段階をカバーする十分な数の組立機が すべての原料をある製品(たとえばパイプ)に加工する。 これらの機械には品質モジュールが付いており、品質アップグレードの確率は10%だ。
あるアイテムがどこへ行きうるか追ってみよう:
ある段階の産物は、同じ段階かそれより低い段階の原料から来る。 その結果の総確率は、それを得る各種の(互いに独立な)経路の確率の和だ。 そして各経路の確率は、ある品質遷移の割合確率に 対応する段階の原料が元々出現する確率を掛けたものだ:
(パーセント記号は暗黙の 100 による除算と読めるので、 「百分率」は掛け算と同じ意味になる。)
ここで百分率の係数は、wiki にある品質遷移確率表と比べると対角線に沿って転置しているように見えるが、 それは単に製品グレードを縦に並べているからにすぎない。 同じアイテムの異なるグレードの確率をまとめて行ベクトルに入れてみよう:
これで、我々の連立一次方程式は、wiki の表と一致する遷移行列をベクトルに掛けるという一つの式として書ける:
境界ケースとして、品質確率がゼロなら品質のグレードアップは起きない。 対応する遷移行列は単位行列になる:
大した進歩には見えないかもしれないが、 これで行列の連続的な積をとれば多段階のプロセスを計算できる。 たとえば鉄鉱石を 7.5% の品質確率で採掘し、 それを 5% の品質確率で鉄板に精錬し、さらに 10% の確率でパイプを作る。 生産性ボーナスがなければ、最終製品が得られる確率はこうなる:
品質戦略
品質モジュールを使えるだけ使えば、あらゆる階層で、いたるところで、同時に問題に対処することもできるが、ここではより小さな自己完結型のシステムに焦点を絞る。
「ギャンブル」:回収しない機会的品質
最も単純だが最も効率の悪い方法は、普通品質の原料に品質モジュールを組み合わせて製造し、何も回収しないというものだ。リサイクラーを解放する前でも実行できるが、少量のアイテムにしか向かない。たとえば星际採集器を数百個作って、そのうち十数個のレアまたは希少品質が出れば、序盤の宇宙船に使える。
工場が普通品質のアイテムを別の用途に使えるなら、この方法は改善する。たとえば、地上に数千枚の普通品質ソーラーパネルを設置し、品質モジュールで製造すれば、出力バッファが埋まる前に、宇宙船向けにより高品質なものが多く得られる。
単一ステップでループもないため、計算は最も簡単だ。期待される製品階層の分布は、遷移行列の1行目、あるいはWikiの確率表の1行目そのものになる。
「洗浄」:純粋な回収ループ
ほとんどのアイテムでは、回収は主要な製造レシピを逆転させ、原料の25%を返す。しかし、製造レシピを持たないアイテム(鉱石など)や、不可逆と見なされるもの(通常は製錬や化学プロセス)もある。この場合、リサイクラーは何も出力しないか、25%の確率で同じアイテムを出力する。リサイクラーに品質モジュールを装着すれば、このプロセスで品質を上げられる。ループで繰り返せば、最終的にすべてのアイテムは破壊されるか、望む品質階層に達するまで引き上げられる。自己回収アイテムは決して一般的とは言えないが、数学が簡単なのでここから始めよう。

設定例:鉱石を採掘し(品質モジュール付き)、レア以上になるまで「洗浄」する。
設定例:鉱石を採掘し(品質モジュール付き)、レア以上になるまで「洗浄」する。
システムにアイテムが注入されたと仮定する。ここでは採掘を例にとり、で各品質階層の確率からなる5成分の行ベクトルを表す。
このベクトルを変換すると、新しい確率の和は1より小さくなりうる。暗黙の残余は、どこにもアイテムが存在しないケースだ。たとえば、リサイクラーが何も出力しない確率が75%である場合、確率ベクトルにを掛けることで表せる。
品質によるフィルタリングも同様に行列の積で表せる。レア以上を抽出する場合を例にとると:
1回のループ反復が総合的に生む効果は次のとおりだ。まず選別して残し、次に品質に応じて回収・再利用する:
ここで、あるアイテムが抽出されうるいくつかのケースを考える:
- もともと品質が十分に高く、すぐに抽出される場合:確率ベクトル
- ちょうど1回のリサイクルループを経る場合:
- ちょうど2回のリサイクルループを経る場合:
- 以下同様。
どんなアイテムも最終的には抽出されるか破棄されるが、 ループの回数に上限はない。 すべての起こりうる結果の組み合わせが、無限級数を構成する:
有限時間で無限和を計算するのは面倒だ。この級数が有限値に収束することは確かで、各項が指数関数的に小さくなるおかげで、項が十分小さくなるまで計算を続ければ近似は得られる。だが厳密解が存在するのにそうするのは釈然としない。
長期的な平均スループット
重要なのは、あるアイテムが何回循環したかではなく、今どの品質ティアにあるかだ。短い時間スケールでは品質モジュールのランダム効果で変動するが、試行回数が十分多ければ確率は比率になる。
そこで個々のアイテムの確率ではなく、十分長い時間でシステムの各部分を通過するアイテムの平均レートを考え、これも品質ティアの5成分ベクトルで表す。これらのベクトルには、これまでと同じ遷移行列をそのまま掛けられる。「洗浄」の純粋なリサイクルループでは、関係するベクトルは次のとおり:
- システムに注入されるアイテム。品質分布は任意で、この例では採掘機の品質モジュールに基づく
- アイテム
- を通過するすべてのアイテム
- 品質ティア のアイテム。この例ではレア以上
- 品質 のアイテム 、つまり抽出されなかった分
と は固定パラメータとし、他のベクトルは未知量として解く。
系は動的平衡に収束し、長期的な平均値は次の方程式を満たす:
並べ替えて代入すると、次のようになります。
転置すると、行ベクトルではなく列ベクトルが得られ、これは古典的な慣習と一致する:
新しい名称をいくつかご紹介します:
ここで均衡状態は古典的なの形の連立一次方程式として表せるので、ガウスの消去法でを解くことができる。からは簡単に計算でき、そこから(必要なリサイクラーの数)と(システム全体の生産率)が求まる。
上の例の構成を毎秒100鉱石の採掘に拡大すると、パラメータは次のようになる。
ガウス消去法による解法は以下のとおりです。
計算すると、抽出レートはおよそ毎分レア90個、エピック9個、レジェンド1個となる。
もう一つ例を見てみよう。
- ノーマル品質の新アイテムのみを注入する。単位はひとまず任意で:
- 現在最良の品質モジュールで回収する:
- レジェンド品質のアイテムのみを抽出する
方程式を解くと、次のようになる。
自分自身をリサイクルするアイテムの場合、「強引な」リサイクルでは、伝説品質の産物を1個生産するごとに、平均して普通品質の入力を個消費する。これは他の人が算出した比率と一致する。必要なリサイクル処理能力は、新規入力レートのをわずかに下回る程度だ。
「アップサイクリング」:クラフト+リサイクルのループ
リサイクルすると実際に原料が返ってくるアイテムなら、クラフターとリサイクラーを直列につないでループにできる:

construction robot を伝説品質までアップサイクルするレイアウト例。
ほとんどのクラフトレシピは複数の原料を、しかもそれぞれ異なる個数要求する。ここではそれは抽象化して、「1回のクラフトに必要な原料一式」をそのまま計量単位として扱う。この例では、construction robot を1台クラフトするマシンは毎秒4個の electronic circuit と2個の flying robot frame を消費するが、これを「毎秒2セットの原料」と呼ぶことにする。
つまり、原料と産物それぞれについて、5つの品質等級ごとの長期的な平均レートを測る。ただし両者はシステムの異なる部分にしか現れないので、5成分ベクトルで十分であり、10次元の数学に踏み込む必要はない。(ふふ、伏線だ。)
レイアウト例では、普通品質の新規原料がロボットによって青いリクエストチェストに運び込まれ、伝説品質の産物が取り出される。一般には、任意の品質分布を持つ原料や産物が別の場所で生産され、このシステムに注入されると考えればよい。同様に、任意の品質等級の産物または原料(あるいは両方)を取り出すこともできる。(原料のほうが産物より高品質で手に入れる価値がある場合もあるが、その特定の産物をアップサイクルしたほうが他の方法より歩留まりが良いこともある。)
さて、考えるべき5成分の行ベクトルは、原料については:
- が系に注入され、品質分布は任意
- 原料
- 、その品質等級ごと、本例ではゼロ
- 原料 が新製品のクラフトに使われる
産物については:
- が系に注入され、品質分布は任意
- 産物
- 品質等級ごとの 、本例では伝説級
- 産物
と は固定パラメータとみなし、 残りのベクトルは解くべき未知量とみなす。
同様に、品質等級で選別する遷移行列は 単位行列の補集合にあたる部分である。 本例では:
製造プロセス を考える。この例ではその値は 0% だが、 一部の製造マシンや生産モジュールを使うと、より高くなる場合がある。 製造時とリサイクル時で品質確率が異なることもある (たとえば生産モジュールを使って製造する場合や、 マシンのモジュールスロット数によって変わる場合など)。
要するに、次のように定める:
システムは以下の平衡状態に収束する:
並べ替えて代入できる:
もう2つ名前を挙げておこう。
またしても、問題を標準的な の形に帰着させた。ここで、
を解くと が得られる。これを元の均衡方程式に代入し直せば、残りの各項もすべて求まる。
上記の例の設定では、パラメータは任意単位でスケーリングされており、具体的には次のとおり。
ガウスの消去法で解け:
実際に動かしてみると、このシステムのボトルネックは普通品質の製品を作る速度にある。 毎分360個だ。 ならば、すべてを倍すればいい。
つまり、このシステムは平均して毎分 2 台近い伝説級建設ロボットを生産し、 毎分およそ 309 セットの原料(フレーム 309 個 + 電子回路 618 個)を消費する。
Space Age では、ごく一部のアイテムに限り、生産性ボーナスを反復研究で上げられる。 ゲーム側でボーナスの上限は +300% に固定されているため、「作っては分解する」 方法で取り戻せるのは、最初に投入した原料と同量までで、それを超えることはない。
生産性研究のレベルごとのコストは指数関数的に増える。 そこで、生産性モジュールも併用して上限まで到達させる前提で考える。 つまり製造機に品質モジュールは入れない。 今度投入するのは原料ではなく完成品だ。
私たちのモデルによる予測:
意外なことに、中間の品質ティアでは生産率がまったく同じになる。
最大の生産性ボーナスは、資源を一切失うことなく通常品質のアイテムを伝説品質に変える。ただし、実用的なスループットに達するには大量の機械とモジュールが必要で、それが代償となる。
「宇宙カジノ」:小惑星再処理ループ
Space Age では、各スペースプラットフォームが小さな工場になる。地上から鉱石を掘るのではなく、小惑星の破片を集めて粉砕し、資源を得る。この資源は船自体の燃料や弾薬に使うこともできるし、宇宙での生産チェーンに供給し、最終製品をいずれかの惑星の地上へ送り返すこともできる。
3 種類の破片(金属質、炭素質、酸化物)はそれぞれ異なる資源を生み出し、太陽系の異なる領域での分布頻度もそれぞれ異なる。小惑星の破片は「再処理」でき、一定の確率で別の種類が得られる(あるいは何も得られない)。

小惑星再処理のレシピ。出典:Factorio wiki
それぞれのレシピに十分な粉砕機があると仮定すれば、この過程を数学的に表せる。行ベクトルに 3×3 の正方遷移行列を掛けるのだ:
再処理は品質モジュールにも対応しているため、「洗浄」と同様に循環利用できる。 伝説級の破片を粉砕すると、一部の基礎資源の伝説級版が産出され、 他の伝説級アイテムの製作に使用できる。
今回は3種類のいずれかの小惑星破片をベルトで輸送する。各種類に5つの品質段階があり、合計15通りのアイテムが存在する。 これを15次元の行ベクトルと15×15の遷移行列で表す。 後者は9つの5×5ブロックからなるブロック行列として構成される:
次元がより高いことを除けば、数学的な導出は洗浄の場合と同じであり、最終的に線形方程式系 が得られ、これを用いて を求める。ここで:
小惑星粉砕機にはモジュールスロットが2つあるため、 再処理で到達できる品質確率の上限は になる。 小惑星収集機にはモジュールスロットがないため、 採取したての破片は常に通常品質だ。 ここでは3つの成分から15成分の ベクトルを組み立てる。各成分は、小惑星の種類(金属質・炭素質・酸化物)ごとの通常品質の産出率に対応する。 たとえば Nauvis 軌道では:
伝説品質の酸化物破片だけを取り出し、残りはすべて再処理すると仮定する。 この方程式を解くと、相補的な15成分の行ベクトルが2つ得られる: と 。これらは3×5の表にまとめられる。
例:Nauvis 軌道で、伝説品質の酸化物破片だけを取り出し、残りはすべて再処理する場合:
| 再処理待ち |
|---|
| 金属質 |
| 炭素質 |
| 酸化物 |
| 抽出済み |
| 金属質 |
| 炭素質 |
| 酸化物 |
ここから分かること:
- 品質レベルが上がるにつれて、小惑星の種類の分布は急速に各 へ収束する。通常品質の新規入力が、システムに入る伝説品質の破片の分布に与える影響は無視できる。
- 単一の種類の伝説級小惑星を抽出する場合、伝説級の産出は新規入力全体のおよそ を占める。これは「洗浄」(つまり純粋なリサイクル)のおよそ よりはるかに良い。再処理が入力を破壊する確率は 20% しかないのに対し、リサイクルは 75% だからだ。
その他の戦略
高品質のアイテムを手に入れる方法は他にもあり、それらは上の分類にうまく当てはまらない(特に「LDS Shuffle」を挙げておく)。今後もっと多くの方法が考案されるだろう。だが、あるループの挙動が計算しにくい場合でも、同じ考え方を使える:
- 複数のアイテムのスループットをベクトルで表す
- 線形変換(製作、リサイクル……)を行列の積で表す
- 動的平衡を行列方程式で表す
- その方程式を解く。必要ならガウスの消去法を使う
インタラクティブな計算ツールを作る
行列の計算を手でやるのは明らかに面倒で、間違いも起きやすい。コンピュータにやらせよう。
習慣でまず Rust で書き始めた。nalgebra はここで必要な行列演算に非常によく合っている。 の連立一次方程式を解くソルバーがいくつも組み込まれているが、それらはほぼすべて、行列とベクトルの成分のスカラー型が浮動小数点数 f32 か f64 であることを要求する。一方、基本ライブラリのほうはもっと汎用的だ。
実際のところ浮動小数点数で十分だが、固定精度の近似値なので、計算の各段階で誤差が入りうる。できるなら正確な結果を得たほうがよくないか? 行列のサイズも演算回数も固定で、しかも比較的小さい。だから速度のためにコードを最適化する必要はない。
使う演算はどれも、突き詰めれば加減乗除だ。
つまりすべてのパラメータが有理数なら、
結果も必ず有理数になる。
num_rational は(汎用の)整数の組で
有理数を正確に表す。
必要なら無限精度の BigInt ライブラリを
持ち込むこともできたが、組み込みの i128 に
オーバーフローチェックを組み合わせれば、
15×15 の行列には十分だ。
(i64 なら 5×5 に対応できる。)
ここまでで、上記の数学計算をすべて実行できる Rust ライブラリはできた。だが、パラメータを調整するのにソースを書き換えるというのは、ユーザー体験としてお世辞にも良いとは言えない。Factoriolab のようなものが欲しい。それに、他の人にも使いやすくするなら、ウェブに置くべきだ。
Rust のコードは WebAssembly にコンパイルできるが、そうすると次のどちらかになる:
- GUI 全体も Rust + wasm で書く。 不可能ではないが、ツールチェーンが まだ使いやすいとは言えない
- GUI は JavaScript か TypeScript で書き(成熟したツールエコシステムを活用できる)、 数学計算のために wasm にブリッジする。 しかしパラメータが多く、API の表面積が小さくないので、 これだけのブリッジを用意するのは楽ではない
そこで、TypeScript で全部書き直すことにした。 BigInt は組み込みで、 Factoriolab にはすでに優れたオープンソースの 有理数ライブラリがあり、 汎用の行列ライブラリを書くのもそれほど難しくない。
ブラウザで動くインタラクティブな GUI については、私が前回フロントエンドをがっつり書いていた頃、jQuery はまだ新しくて流行っているものだった。React を学びたくはなかったので、こう選んだ:
合わせて、factoqual.grebedoc.dev で品質リサイクラーを計画するためのインタラクティブな GUI を公開している。ソースは codeberg.org/SimonSapin/factoqual にある。
謝辞
品質の数学に関する Daniel Monteiro の ブログ記事から大きな刺激を受けた。 他にも Konage や Factorio wiki の貢献者たちなど、 似た取り組みをしている人はいる。 だが、ここで示した平衡方程式を解いて得られる正確な解は、 無限級数を反復近似するのではなく、新しいものだと信じている。