# LLMとプログラミング：委譲の損益分岐点と「もっともらしい誤り」の見抜き方

> LLMを設計・実装・テスト・レビューに組み込む判断を、成功率と検証コストの比という一つの不等式に還元して整理する。得意・不得意が分かれる理由、タスク分割が効く理由、AIのもっともらしい誤りを検出する手順を数値例とともに示す。
> https://rikai.mugen-giken.com/computer-science/ai-era/llm-and-programming

## 0. この記事の要点

- LLM に任せるべきかはモデルの賢さだけでは決まらず、**成功率 $p$、指示を書くコスト、検証するコスト**の三つで決まります。委譲が得になる条件は $p > g + v$（$g$ は指示比、$v$ は検証比）という一つの不等式に還元できます（<Ref to="prop-threshold" />）。
- この不等式から、**検証コストが自作コストと同程度に高いタスクは、モデルがどれだけ賢くなっても委譲では得をしない**という結論が出ます（<Ref to="cor-no-delegation" />）。新規アーキテクチャの設計が LLM に向かないのは、モデルが弱いからではなく $v$ が $1$ に近いからです。
- 失敗したら再生成する、という戦略を採っても損益分岐点は動きません（<Ref to="prop-retry" />）。再生成が効くのは検証が信頼できるときだけです。
- 採用した成果物が誤っている確率は、検証器の**偽陽性率 $\alpha$** にほぼ比例します（<Ref to="thm-acceptance-error" />）。「AI の嘘を見抜く力」とは、この $\alpha$ を下げる技術です。
- 大きなタスクを一括で投げると期待コストは工程数に対して**指数的に**膨らみ、工程ごとに検証すれば線形に抑えられます（<Ref to="thm-decomposition" />）。これが「小さく切って渡す」の定量的な根拠です。

## 1. 動機：なぜ「使える／使えない」で意見が割れるのか

LLM をプログラミングに使った人の感想は、驚くほど両極端に分かれます。「実装速度が倍になった」と言う人と、「結局全部書き直したので遅くなった」と言う人が、同じ会社の同じチームにいます。

この分裂は測定でも再現されています。GitHub Copilot を使って JavaScript の HTTP サーバーを実装させた対照実験では、支援を受けた群がタスクを 55.8% 速く完了しました（Peng et al., 2023）。一方、実務経験の長い開発者が自分の熟知した大規模 OSS リポジトリで作業した実験では、AI 支援を受けた群のほうが 19% 遅くなり、しかも本人たちは「速くなった」と感じていました（METR, 2025）。前者の課題は仕様が明確で正しさの確認が容易であり、後者は被験者が対象コードを熟知していて品質基準も高い、という違いがあります。この違いが結果を分けている、というのがこの記事の見立てです。

同じ技術で正反対の結果が出るのですから、「LLM は開発に役立つか」という問いの立て方が間違っています。正しい問いは**「どういう性質のタスクで役立ち、どういう性質のタスクで害になるのか」**です。そして、その性質を言い当てる変数を見つけなければ、議論は経験談の応酬で終わってしまいます。

この記事では、その変数を三つに絞り込みます。仕事を外部に委ねるかどうかの判断は、経済学でいう取引コストの問題です。外注先がどれだけ優秀でも、**仕様を説明する手間と、上がってきた成果物を検品する手間**が自分でやるより高ければ、外注は損になります。LLM も例外ではありません。違うのは、LLM の検品コストが人間の外注先より高くつきやすいという点です。人間の下請けは分からないところを質問しますが、LLM は分からないところを**それらしく埋めて**返してくるからです。

以下ではこの構造を最小限のモデルにし、そこから「得意・不得意」「タスク分割」「デバッグ技術」の三つを順に導きます。前提となるエンジニア像の議論は [AI時代のITエンジニアの生存戦略](/computer-science/ai-era/engineer-survival-strategy) を参照してください。とくに、工程ごとの自動化率が全体の短縮率をどこまでしか押し上げられないかは <Ref to="computer-science/ai-era/engineer-survival-strategy#cor-amdahl-limit" text="自動化の上限" /> にあります。

## 2. 準備：委譲をコストでモデル化する

議論の対象は「ひとまとまりの作業単位」です。関数を一つ書く、テストを一式書く、API 仕様書を作る、といった粒度を想定してください。

<Definition id="def-delegation-cost" title="委譲コストモデル">
ひとつの作業単位 $T$ について、次の量を定めます。単位は時間でも金額でも構いませんが、記事全体で統一します。

- $c_h > 0$：$T$ を人間が最初から自分で仕上げるコスト（設計・記述・自己点検を含む）。
- $c_g \ge 0$：LLM に $T$ を依頼するための指示（プロンプト・文脈の提示）を書くコスト。
- $c_v \ge 0$：LLM の出力を検証するコスト。読む、実行する、テストする、仕様と突き合わせる、の総和。
- $p \in [0, 1]$：LLM の出力が検証を通る確率（**成功率**）。

さらに、比として
$$
g = \frac{c_g}{c_h}, \qquad v = \frac{c_v}{c_h}
$$
を定め、それぞれ**指示比**、**検証比**と呼びます。
</Definition>

$c_h$ で割って無次元化するのが要点です。同じ「検証に 20 分かかる」でも、自作に 2 時間かかるタスクなら $v = 1/6$ で軽い作業ですが、自作に 25 分のタスクなら $v = 0.8$ で致命的に重い作業になります。効くのは絶対時間ではなく比です。

検証は「通る／通らない」の二値判定として扱います。この判定を行う主体を検証器と呼びます。

<Definition id="def-verifier" title="検証器と偽陽性率">
出力を受け取って「合格」「不合格」を返す手続きを**検証器**と呼びます。テストスイート、型検査器、静的解析、人間のレビュー、およびそれらの組み合わせが該当します。

検証器について、
- **偽陰性率**：正しい出力を不合格にする確率。以下では簡単のため $0$ と仮定します。
- **偽陽性率 $\alpha \in [0, 1]$**：誤った出力を合格にする確率。

$\alpha$ は検証器の**穴の大きさ**です。テストが 3 ケースしかなければ $\alpha$ は大きく、境界値・退化ケース・並行実行まで網羅していれば小さくなります。
</Definition>

<Aside type="note">
偽陰性率を $0$ と置くのは理想化です。正しいコードが不合格になることは実際にありますが、それは「やり直し」を増やすだけで、**誤ったものを本番に通す**という失敗にはつながりません。重要なのは後者なので、前者は落とします。
</Aside>

## 3. 委譲の損益分岐点

### 3.1. 一回だけ試す場合

<Proposition id="prop-threshold" title="委譲が得になる条件">
<Ref to="def-delegation-cost" text="委譲コストモデル" /> のもとで、次の戦略を考えます。

> LLM に一度依頼し、出力を検証する。合格すれば採用して終了する。不合格なら出力を捨て、人間が最初から自分で仕上げる。

この戦略の期待コスト $C_1$ は
$$
C_1 = c_g + c_v + (1 - p)\,c_h
$$
であり、$C_1 < c_h$ が成り立つのは
$$
p > g + v
$$
のとき、かつそのときに限ります。
</Proposition>

<Proof of="prop-threshold">
指示と検証は結果によらず必ず行うので、コスト $c_g + c_v$ は確率 $1$ で発生します。追加で人間が書き直す事象の確率は $1 - p$（<Ref to="def-delegation-cost" /> の $p$ の定義より、検証を通らない確率）であり、そのときのコストは $c_h$ です。期待値の線形性から
$$
C_1 = (c_g + c_v) \cdot 1 + c_h \cdot (1 - p) = c_g + c_v + (1-p)c_h .
$$

次に不等式を変形します。
$$
\begin{aligned}
C_1 < c_h
&\iff c_g + c_v + (1-p)c_h < c_h \\
&\iff c_g + c_v < c_h - (1-p)c_h = p\,c_h .
\end{aligned}
$$
$c_h > 0$ なので両辺を $c_h$ で割れます。不等号の向きは変わりません。
$$
\frac{c_g}{c_h} + \frac{c_v}{c_h} < p ,
$$
すなわち <Ref to="def-delegation-cost" /> の記号で $g + v < p$ です。すべて同値変形なので逆も成り立ちます。
</Proof>

この不等式は、読み下すと当たり前のことを言っています。**「指示を書く手間と検品の手間の合計が、成功率で割り引いた自作コストより小さいなら任せる」**。当たり前ですが、当たり前でない帰結を持ちます。

<Corollary id="cor-no-delegation" title="検証が高いタスクは委譲不能">
$g + v \ge 1$ であるようなタスクは、成功率 $p$ がどれほど高くても $C_1 \ge c_h$ となり、委譲によって得をすることはありません。
</Corollary>

<Proof of="cor-no-delegation">
確率の定義から $p \le 1$ です。仮定より $g + v \ge 1 \ge p$ なので、$p > g+v$ は成り立ちません。<Ref to="prop-threshold" /> は同値条件なので、$C_1 < c_h$ も成り立ちません。
</Proof>

<Ref to="cor-no-delegation" /> は短い主張ですが、この記事でいちばん重要な結論です。ここには $p$ が現れません。つまり**モデルの性能向上ではこの壁を越えられない**ということです。越えられるのは、$v$ を下げる工夫（検証を安くする。ただし下げ幅には <Ref to="computer-science/ai-era/engineer-survival-strategy#cor-verify-floor" text="検証コストの床" /> があります）か、$c_h$ を上げる状況（自作が本当に大変なタスクを選ぶ）だけです。

### 3.2. 何度も試す場合

「一度で駄目なら捨てる」というのは現実的ではありません。ふつうは条件を足して再生成します（<Ref to="computer-science/ai-era/engineer-survival-strategy#def-generate-verify" text="生成検証サイクル" />）。ところが、それでも境界は動きません。

<Proposition id="prop-retry" title="再生成しても損益分岐点は変わらない">
<Ref to="def-delegation-cost" text="委譲コストモデル" /> のもとで、各回の生成が互いに独立で、いずれも成功率 $p \in (0,1]$ を持つとします。次の戦略を考えます。

> 生成と検証を最大 $k$ 回まで繰り返す。途中で合格すればそこで終了する。$k$ 回すべて不合格なら、人間が最初から自分で仕上げる。

このとき期待コストは
$$
C_k = (c_g + c_v)\,\frac{1 - (1-p)^k}{p} + (1-p)^k c_h
$$
であり、数列 $(C_k)_{k \ge 1}$ は
- $p > g + v$ のとき狭義単調減少で、$k \to \infty$ の極限は $(c_g + c_v)/p < c_h$、
- $p < g + v$ のとき狭義単調増加で、すべての $k$ について $C_k > c_h$、
- $p = g + v$ のとき定数で、すべての $k$ について $C_k = c_h$

となります。とくに、ある $k$ で $C_k < c_h$ となることと $p > g+v$ とは同値です。
</Proposition>

<Proof of="prop-retry">
第 $i$ 回目の生成が実行されるのは、$1$ 回目から $i-1$ 回目までがすべて不合格だった場合に限ります。独立性の仮定からその確率は $(1-p)^{i-1}$ です。第 $i$ 回目を実行するときは必ず $c_g + c_v$ を払うので、期待値の線形性より生成・検証の期待コストは
$$
\sum_{i=1}^{k} (1-p)^{i-1}(c_g + c_v) = (c_g+c_v)\,\frac{1-(1-p)^k}{1-(1-p)} = (c_g+c_v)\,\frac{1-(1-p)^k}{p}
$$
です（等比数列の和、公比 $1-p$、$p \ne 0$ より分母は $0$ でない）。人間が書き直すのは $k$ 回すべて不合格の場合だけで、その確率は $(1-p)^k$、コストは $c_h$ です。両者を足して主張の式を得ます。

単調性は差を取れば分かります。
$$
\begin{aligned}
C_{k+1} - C_k
&= (c_g+c_v)\,\frac{(1-p)^k - (1-p)^{k+1}}{p} + \left[(1-p)^{k+1} - (1-p)^k\right] c_h \\
&= (c_g+c_v)\,\frac{(1-p)^k \, p}{p} - (1-p)^k p \, c_h \\
&= (1-p)^k \left[(c_g + c_v) - p\,c_h\right].
\end{aligned}
$$
ここで $(1-p)^k > 0$（$p < 1$ のとき。$p = 1$ なら $C_k$ は $k \ge 1$ で定数 $c_g+c_v$ となり、主張は <Ref to="prop-threshold" /> に帰着します）。したがって差の符号は $(c_g+c_v) - p\,c_h$ の符号に一致し、これは <Ref to="prop-threshold" /> の証明中の変形から $g + v$ と $p$ の大小に一致します。

$k \to \infty$ では $|1-p| < 1$ より $(1-p)^k \to 0$ なので $C_k \to (c_g+c_v)/p$ です。$p > g+v$ すなわち $c_g + c_v < p\,c_h$ のとき、これは $c_h$ より小さくなります。$C_1 = c_h$ となるのが $p = g+v$ のちょうどの場合であることは <Ref to="prop-threshold" /> から従い、そこから単調性で全体が決まります。
</Proof>

実務的な含意は明快です。**一度目で駄目だったものを粘って直させるかどうかは、判断の本質ではありません**。粘るべきタスクは一度目から任せてよく、粘るべきでないタスクは一度目から任せるべきでないのです。「もう少しで動きそうだ」という感覚に従って三度四度と再生成を繰り返し、気づけば自分で書いたほうが速かった、という体験の正体がこれです。$p < g+v$ の領域では、再試行は状況を悪化させる一方です。

<Aside type="caution">
<Ref to="prop-retry" /> は「各回の生成が独立」という仮定に立っています。失敗したテストの出力を LLM に見せて修正させる場合、この仮定は破れます。破れ方には良い面と悪い面の両方があり、悪い面は <Ref to="rem-independence" /> で扱います。
</Aside>

### 3.3. 判断を一枚の図にする

$p$ を横軸、$v$ を縦軸に取ると、<Ref to="prop-threshold" /> の条件は「直線 $v = p - g$ より下」という半平面になります。指示比 $g$ を $0.05$ と置いた図を示します。

<Figure caption="委譲の損益分岐（横軸：成功率 p、縦軸：検証比 v、指示比 g = 0.05）。点の位置は説明のための概算です。">
<svg viewBox="0 0 640 400" width="100%" role="img" aria-label="成功率と検証比の平面上で、委譲が得になる領域と損になる領域を分ける境界線">
  <line x1="60" y1="340" x2="610" y2="340" stroke="currentColor" stroke-width="1.5" />
  <line x1="60" y1="340" x2="60" y2="30" stroke="currentColor" stroke-width="1.5" />
  <polygon points="87,340 600,340 600,55" fill="currentColor" opacity="0.07" />
  <line x1="87" y1="340" x2="600" y2="55" stroke="var(--sl-color-accent)" stroke-width="2.5" />
  <circle cx="519" cy="295" r="5" fill="var(--sl-color-accent)" />
  <circle cx="546" cy="310" r="5" fill="var(--sl-color-accent)" />
  <circle cx="465" cy="265" r="5" fill="var(--sl-color-accent)" />
  <circle cx="384" cy="295" r="5" fill="var(--sl-color-accent)" />
  <circle cx="249" cy="160" r="5" fill="var(--sl-color-accent)" />
  <circle cx="195" cy="70" r="5" fill="var(--sl-color-accent)" />
  <text x="509" y="291" text-anchor="end" font-size="13" fill="currentColor">定型コード生成</text>
  <text x="536" y="328" text-anchor="end" font-size="13" fill="currentColor">ドキュメント生成</text>
  <text x="455" y="261" text-anchor="end" font-size="13" fill="currentColor">テスト生成</text>
  <text x="374" y="316" text-anchor="end" font-size="13" fill="currentColor">局所的なコードレビュー</text>
  <text x="261" y="164" text-anchor="start" font-size="13" fill="currentColor">複雑な並行処理の実装</text>
  <text x="207" y="74" text-anchor="start" font-size="13" fill="currentColor">新規アーキテクチャの決定</text>
  <text x="72" y="122" text-anchor="start" font-size="13" fill="currentColor" opacity="0.75">自分で書くほうが安い</text>
  <text x="592" y="150" text-anchor="end" font-size="13" fill="currentColor" opacity="0.75">委譲するほうが安い</text>
  <text x="592" y="42" text-anchor="end" font-size="12" fill="var(--sl-color-accent)">境界 v = p − g</text>
  <text x="60" y="362" text-anchor="middle" font-size="12" fill="currentColor">0</text>
  <text x="330" y="362" text-anchor="middle" font-size="12" fill="currentColor">0.5</text>
  <text x="600" y="362" text-anchor="middle" font-size="12" fill="currentColor">1</text>
  <text x="600" y="382" text-anchor="end" font-size="13" fill="currentColor">成功率 p</text>
  <text x="44" y="40" text-anchor="middle" font-size="13" fill="currentColor">検証比 v</text>
</svg>
</Figure>

<Remark id="rem-estimates">
図中の点の座標は概算であって測定値ではありません。主張したいのは個々の数値ではなく、**タスクが平面上のどこに位置するかで判断が決まる**という構造です。自分のチームでの実際の位置を知りたければ、しばらく「指示を書いた時間」「レビューに使った時間」「差し戻した回数」を記録してください。この三つから $g$、$v$、$p$ の推定値が得られます。
</Remark>

## 4. 得意・不得意の地図

<Ref to="prop-threshold" /> と <Ref to="cor-no-delegation" /> を使って、よくある作業を分類します。判定に必要なのは「モデルがどれくらい賢いか」ではなく、**そのタスクの正しさをどれくらい安く確かめられるか**です。

| 作業 | 成功率 $p$ | 検証比 $v$ | 判定 | 検証が安い／高い理由 |
|---|---|---|---|---|
| 定型的なコード生成（DTO 変換、CRUD、設定ファイル） | 高 | 低 | 委譲する | 実行すれば分かる。仕様が閉じており、想定外の入力が少ない |
| ドキュメント・コメント・コミットメッセージ | 高 | 低 | 委譲する | 正しさの基準がコード本体にあり、突き合わせが速い |
| 既知のアルゴリズムの実装 | 中〜高 | 低 | 委譲する | 参照実装・既知の性質（不変量）と照合できる |
| テストコードの生成 | 中 | 低〜中 | 条件付きで委譲する | 実行できるが、「何を検査していないか」の確認が要る |
| 局所的なコードレビュー（規約違反、未処理の例外、既知の脆弱性パターン） | 中 | 低 | 委譲する | 指摘を一つ見れば真偽が判定できる。誤指摘のコストが小さい |
| 既存コードの機械的リファクタリング | 中 | 中 | 場合による | テストの網羅度に完全に依存する |
| 複雑な並行処理・分散処理の実装 | 低 | 高 | 委譲しない | テストを通っても正しさが保証されない。再現しない誤りが残る |
| 性能要件を伴う設計判断 | 低 | 高 | 委譲しない | 判定に負荷試験か解析が必要で、それ自体が本作業 |
| 新規性の高いアーキテクチャの決定 | 低 | $\approx 1$ | 委譲しない | 判定するには自分で設計するのと同じ思考が要る |

最下段の 3 行に注目してください。共通しているのは「$p$ が低い」ことではなく、「$v$ が高い」ことです。この違いは重要です。$p$ が低いだけなら、モデルの進歩が解決してくれます。$v$ が高い場合は <Ref to="cor-no-delegation" /> により解決しません。

<Example id="ex-crud" title="定型コードの数値評価">
社内 API のレスポンス型からフロントエンド用の型定義と変換関数を書く作業を考えます。手で書けば 90 分（$c_h = 90$）、API 仕様を貼り付けて指示を書くのに 8 分（$c_g = 8$）、生成物を読んで型検査を通し既存のテストを走らせるのに 12 分（$c_v = 12$）とします。

$$
g + v = \frac{8 + 12}{90} = \frac{20}{90} \approx 0.222 .
$$

したがって、<Ref to="prop-threshold" /> より、成功率が $22.2\%$ を超えれば委譲が得です。実際には $p \approx 0.8$ 程度は期待できますから、余裕をもって条件を満たします。無限に再試行できる場合の期待コストは <Ref to="prop-retry" /> より
$$
\frac{c_g + c_v}{p} = \frac{20}{0.8} = 25 \ \text{分}
$$
で、自作の $90$ 分に対しておよそ $3.6$ 倍の効率です。この種のタスクで「劇的に速くなった」という体験が生まれます。
</Example>

<Example id="ex-new-architecture" title="新規アーキテクチャ設計の数値評価">
これまで単一データベースで動いていたサービスを、地域ごとに分割して結果整合で運用する構成に移行するか判断する、という作業を考えます。自分で結論を出すには、障害モードの列挙、整合性要件の棚卸し、移行計画の粗い見積もりが必要で、$c_h = 40$ 時間とします。

LLM に「この要件で最適な構成を提案して」と依頼するのは 0.5 時間で済みます（$g = 0.0125$）。問題は検証です。返ってきた構成が要件を満たすかを判断するには、結局、障害モードの列挙も整合性要件の棚卸しも自分でやらなければなりません。楽になるのは「選択肢を思いつく」段階だけですから、$c_v = 35$ 時間、$v = 0.875$ と見積もられます。

$$
g + v = 0.0125 + 0.875 = 0.8875 .
$$

$p > 0.8875$、つまり**一発で採用できる設計が返ってくる確率が 9 割**でなければ、委譲は損になります。現実の成功率はこれよりはるかに低いので、この作業は委譲に向きません。しかも仮に $c_v$ の見積もりが甘く $c_v = 40$ だったなら $g + v > 1$ となり、<Ref to="cor-no-delegation" /> よりどんなモデルでも救えません。
</Example>

<Remark id="rem-not-delegation">
<Ref to="ex-new-architecture" /> の結論は「設計に LLM を使うな」ではありません。**委譲という使い方をするな**、です。同じ場面でも、「この構成で起きうる障害モードを 20 個挙げてください」という使い方なら話が変わります。この場合、成果物は候補リストであり、各項目の妥当性は一つずつ独立に、しかも短時間で判定できます。つまり $v$ が劇的に下がり、なおかつ $c_h$（自分だけで 20 個挙げる手間）は小さくありません。

要点は、**高い $v$ のタスクを、低い $v$ の部分タスクへ翻訳する**ことです。「答えを出させる」のではなく「見落としを潰させる」「選択肢を広げさせる」形に問いを変換する。これが設計工程における LLM の正しい使い方だと考えています。
</Remark>

## 5. タスクを切る

「大きな仕事を一度に投げず、小さく切って渡せ」という助言は経験則としてよく語られます。これは定理として証明できます。

<Definition id="def-decomposition" title="工程分解と工程正答率">
作業 $T$ が $n$ 個の工程 $T_1, \dots, T_n$ に分解され、各工程について生成と検証にかかるコストの合計が $1$（単位コスト）であるとします。各工程の出力が正しい確率を $r \in (0, 1)$ とし、異なる工程の正誤および同一工程の再試行は互いに独立であるとします。この $r$ を**工程正答率**と呼びます。
</Definition>

<Theorem id="thm-decomposition" title="逐次検証の優位性">
<Ref to="def-decomposition" text="工程分解" /> のもとで、検証は完全（偽陽性率 $\alpha = 0$、偽陰性率 $0$）とし、次の二つの方式を比較します。

- **一括方式**：$n$ 工程をまとめて一度に生成し、全体をまとめて検証する。不合格なら全体を破棄して最初から生成し直す。合格するまで繰り返す。
- **逐次方式**：工程 $T_1, \dots, T_n$ をこの順に生成し、各工程の生成直後にその工程だけを検証する。不合格ならその工程だけを生成し直す。合格したら次の工程へ進む。

このとき、完成までの期待総コストはそれぞれ
$$
E_{\text{一括}} = \frac{n}{r^{\,n}}, \qquad E_{\text{逐次}} = \frac{n}{r}
$$
であり、その比は
$$
\frac{E_{\text{一括}}}{E_{\text{逐次}}} = \left(\frac{1}{r}\right)^{n-1}
$$
となります。すなわち一括方式のコストは工程数 $n$ について**指数的に**増大します。
</Theorem>

<Proof of="thm-decomposition">
まず補助的な事実を確認します。成功確率 $s \in (0,1]$ の独立試行を成功するまで繰り返すとき、試行回数 $N$ は幾何分布に従い、$\mathbb{E}[N] = 1/s$ です。実際、$\Pr[N = i] = (1-s)^{i-1}s$ より
$$
\mathbb{E}[N] = \sum_{i=1}^{\infty} i (1-s)^{i-1} s = s \cdot \frac{1}{(1-(1-s))^2} = \frac{1}{s}
$$
（$\sum_{i \ge 1} i x^{i-1} = (1-x)^{-2}$、$|x| < 1$ を $x = 1-s$ に適用）。

**一括方式**。$n$ 工程すべてが正しい確率は、独立性の仮定（<Ref to="def-decomposition" />）より $r^n$ です。検証は完全なので、合格することと全体が正しいことは同値であり、1 回の試行の成功確率は $s = r^n$ です。1 回の試行では $n$ 工程すべてを生成・検証するのでコストは $n$。したがって期待総コストは $n \cdot \mathbb{E}[N] = n / r^n$ です。

**逐次方式**。工程 $T_i$ に着目します。1 回の試行の成功確率は $s = r$、1 回の試行のコストは $1$ なので、$T_i$ を完成させるまでの期待コストは $1/r$ です。工程は互いに独立で、しかも各工程は前の工程が確定してから始まるため、総コストは各工程のコストの和です。期待値の線形性から
$$
E_{\text{逐次}} = \sum_{i=1}^{n} \frac{1}{r} = \frac{n}{r} .
$$

比を取ると $\dfrac{n/r^n}{n/r} = r^{1-n} = (1/r)^{n-1}$ を得ます。
</Proof>

<Example id="ex-split" title="工程数と一括方式の損失">
工程正答率 $r = 0.98$（一つの工程を 98% の確率で正しく仕上げる、かなり優秀なモデル）としたときの比 $(1/0.98)^{n-1}$ を計算します。$\ln(1/0.98) = 0.020203$ を使います。

| 工程数 $n$ | 指数 $(n-1)\ln(1/r)$ | 一括方式のコスト倍率 |
|---|---|---|
| $10$ | $0.1818$ | $1.20$ 倍 |
| $50$ | $0.9899$ | $2.69$ 倍 |
| $200$ | $4.0204$ | $55.7$ 倍 |

工程 10 個なら一括で投げても 2 割増しに過ぎず、体感では差が出ません。ところが 200 個になると 56 倍です。「小さな試作では快適だったのに、実際の機能開発に使うと破綻する」という現象は、この指数関数で説明できます。モデルの $r$ を $0.98$ から $0.99$ に上げても、$n = 200$ での倍率は $(1/0.99)^{199} = e^{2.0} \approx 7.4$ 倍で、依然として大きい。**分割は、モデルの改善よりも効きます。**
</Example>

<Remark id="rem-decomposition-limit">
<Ref to="thm-decomposition" /> の逐次方式が成立するには、「工程 $T_i$ の誤りが $T_i$ 単独で検出できる」ことが必要です。個々の関数は正しいのに組み合わせるとインタフェースの解釈がずれていて壊れる、という誤りはこの仮定の外にあります。

したがって実務では、工程分割と同時に**結合部の検証**（統合テスト、契約テスト、型による境界の固定）を用意しなければなりません。分割の利得は、境界を明示的に定義するコストと引き換えに得られます。境界を固定することで一度に検査すべき対象がどれだけ減るかは <Ref to="computer-science/ai-era/engineer-survival-strategy#prop-modularity" text="モジュール分割による検査対象の削減" /> にあります。裏を返せば、境界が明確でないコードベースでは分割の効果が薄く、LLM の導入効果も出にくくなります。
</Remark>

## 6. 開発プロセスへの組み込み

以上を踏まえて、工程ごとの具体的な使い方を整理します。方針は一貫しています。**$v$ を下げる形に問いを変換し、$n$ を小さく切り、検証を先に用意する。**

<Figure caption="生成と検証のループ。偽陽性の経路（右下）だけが本番に誤りを運びます。">
<Mermaid code={`flowchart TD
  A["仕様と受け入れ条件を人が書く"] --> B["LLM が候補を生成"]
  B --> C&#123;"検証器にかける"&#125;
  C -->|"不合格"| B
  C -->|"合格"| D["採用"]
  D --> E["本当に正しい"]
  D --> F["偽陽性: もっともらしい誤りが残る"]`} />
</Figure>

**設計。** 答えを出させず、材料を出させます。「この要件を満たす構成案を 3 つ、それぞれの失敗モードとともに挙げてください」「この設計で最初に壊れるのはどこですか」「この仕様に書かれていない前提を列挙してください」。いずれも出力が箇条書きで、一項目ずつ独立に真偽を判定できます。<Ref to="rem-not-delegation" /> の翻訳を実行しているわけです。決定そのものは人間が行います。決定を委ねた瞬間に $v$ が跳ね上がるからです。

**実装。** 工程を切り、検証を**先に**用意します。テストを先に書くと、$c_v$ が「テストを実行する時間」まで下がります。<Ref to="ex-crud" /> で $v$ が小さかったのは、まさに既存のテストと型検査があったからです。逆に、テストのないコードベースに LLM で機能を追加すると、$c_v$ は「生成された全行を人間が読む時間」になります。これは往々にして自分で書く時間を超えます。テスト駆動開発が LLM 時代に再評価されているのは、思想の問題ではなく <Ref to="prop-threshold" /> の問題です。

**テスト。** LLM にテストを書かせるのは有効ですが、一つ落とし穴があります。**同じモデルに実装とテストの両方を、同じ文脈で書かせてはいけません。** モデルが実装で誤解した仕様は、テストでも同じように誤解されます。すると誤った実装が誤ったテストを通り、偽陽性率 $\alpha$ が跳ね上がります。検証器は、検証される対象と独立でなければ意味がありません。実務的には、テストは仕様書から（実装コードを見せずに）生成する、期待値は人間が手で埋める、境界値だけは自分で列挙する、といった分離が有効です。

**レビュー。** LLM によるコードレビューは、実は最も費用対効果の高い使い方です。理由は $v$ の小ささにあります。「この行で `null` が来る可能性があります」という指摘は、その行を見れば数秒で真偽が分かります。誤指摘が混ざっても、失うのは数十秒であって本番の障害ではありません。**指摘は間違っていてもよいが、コードは間違っていては困る**という非対称性が、レビュー用途を有利にしています。ただし「この抽象化はこの先 2 年の変更に耐えるか」といった設計レベルの問いは <Ref to="ex-new-architecture" /> と同じ構造で、検証が高くつきます。

## 7. AI の「嘘」を見抜く

ここまでは検証器を所与としてきました。最後に、検証器そのものの質を扱います。プログラムの意味的な性質を機械的に完全に判定することは <Ref to="computer-science/ai-era/engineer-survival-strategy#thm-rice" text="ライスの定理" /> によって不可能ですから、現実の検証器の偽陽性率 $\alpha$ を $0$ にすることはできません。ここが LLM 時代のデバッグ技術の中心です。

<Definition id="def-plausible-error" title="もっともらしい誤り">
LLM の出力のうち、**形式的な整合性**（構文が通る、型が合う、命名規則に従っている、説明が自然な日本語や英語になっている）を満たしながら、**意味的に偽**であるものを、この記事では**もっともらしい誤り**と呼びます。存在しない API の呼び出し、境界条件を落とした実装、実際とは異なる出典の引用などが該当します。
</Definition>

もっともらしい誤りが厄介なのは、人間の第一印象という検証器を素通りする点にあります。構文の乱れは注意を引きますが、もっともらしい誤りは引きません。むしろ、整った命名と丁寧なコメントがついているぶん、自分で書いたコードより信頼されがちです。実験でもこの効果が観測されています。AI 支援を受けた被験者は、支援なしの被験者より安全性の低いコードを書いたにもかかわらず、**自分は安全なコードを書けたと信じる傾向が強かった**という報告があります（Perry et al., 2023）。これは $\alpha$ の上昇そのものです。

では $\alpha$ はどれだけ効くのでしょうか。定量的に答えられます。計算の構造は、検査が陽性と言ったときの的中率を求める問題（<Ref to="computer-science/ai-era/relearning-mathematics#ex-bayes-ppv" text="検知率 99%、誤検知率 1% のモデルが出す警告の信頼度" />）と同じものです。

<Theorem id="thm-acceptance-error" title="採用物の誤り率">
<Ref to="def-verifier" text="検証器" /> の偽陽性率を $\alpha \in [0,1]$、偽陰性率を $0$ とします。各回の生成は独立で、出力が正しい確率は $p \in (0,1)$ とします。合格が出るまで生成を繰り返し、**最初に合格した出力を採用する**とき、少なくとも 1 回は合格が出るという条件のもとで、採用された出力が誤りである確率は
$$
P_{\text{err}} = \frac{(1-p)\,\alpha}{p + (1-p)\,\alpha}
$$
です。とくに $\alpha$ が小さいとき
$$
P_{\text{err}} \approx \frac{1-p}{p}\,\alpha
$$
と近似され、**誤り率は偽陽性率にほぼ比例します**。この値は試行回数の上限に依存しません。
</Theorem>

<Proof of="thm-acceptance-error">
1 回の生成について、次の三つは互いに排反で、これで全事象を尽くします。

- 正しく、かつ合格する：確率 $p$（偽陰性率 $0$ の仮定より、正しい出力は必ず合格します）。
- 誤りで、かつ合格する：確率 $(1-p)\alpha$（<Ref to="def-verifier" /> の $\alpha$ の定義）。
- 誤りで、かつ不合格：確率 $(1-p)(1-\alpha)$。

したがって 1 回の生成が合格する確率は $q = p + (1-p)\alpha$ です。$p > 0$ より $q > 0$ です。

「最初の合格が第 $i$ 回目に起きる」という事象の確率は、独立性より $(1-q)^{i-1}q$ です。その合格が誤りである確率は、第 $i$ 回目の生成が「誤りかつ合格」である確率と「合格」である確率の比、すなわち $(1-p)\alpha / q$ で、$i$ に依存しません。よって全確率の公式から
$$
P_{\text{err}} = \frac{\sum_{i \ge 1} (1-q)^{i-1}(1-p)\alpha}{\sum_{i \ge 1} (1-q)^{i-1} q} = \frac{(1-p)\alpha \cdot \frac{1}{q}}{q \cdot \frac{1}{q}} = \frac{(1-p)\alpha}{q} = \frac{(1-p)\alpha}{p + (1-p)\alpha}.
$$
ここで両方の級数は $0 < q \le 1$ より収束します。

近似は、$\alpha \to 0$ のとき分母が $p + O(\alpha) \to p$ となることから従います。
</Proof>

<Example id="ex-error-table" title="偽陽性率と成功率、どちらを改善すべきか">
<Ref to="thm-acceptance-error" /> の式に数値を入れます。$P_{\text{err}} = (1-p)\alpha / (p + (1-p)\alpha)$ をそのまま計算します。

| $p$ | $\alpha$ | 計算 | $P_{\text{err}}$ |
|---|---|---|---|
| $0.4$ | $0.40$ | $0.24 / (0.40 + 0.24)$ | $37.5\%$ |
| $0.7$ | $0.40$ | $0.12 / (0.70 + 0.12)$ | $14.6\%$ |
| $0.4$ | $0.05$ | $0.03 / (0.40 + 0.03)$ | $7.0\%$ |
| $0.7$ | $0.05$ | $0.015 / (0.70 + 0.015)$ | $2.1\%$ |

1 行目を基準にすると、モデルを賢くして $p$ を $0.4$ から $0.7$ に上げた場合の改善は $37.5\% \to 14.6\%$、検証を厳しくして $\alpha$ を $0.40$ から $0.05$ に下げた場合の改善は $37.5\% \to 7.0\%$ です。後者のほうが大きい。しかも $p$ を上げるにはモデルを取り替えるしかないのに対し、$\alpha$ を下げるのは自分の手でできます。**テストを 1 ケース足すほうが、モデルを乗り換えるより効く場面が多い**、というのがこの表の読み方です。
</Example>

<Remark id="rem-independence">
<Ref to="thm-acceptance-error" /> は各回の生成が独立であることを仮定しています。失敗したテストの出力を LLM に渡して修正させる運用では、この仮定が破れます。しかも破れ方が悪い方向です。モデルは**そのテストを通すこと**を目標に出力を調整するので、テストが検査していない部分の正しさは保証されなくなります。極端な場合、期待値をハードコードして通してしまいます。実効的な $\alpha$ が上がるわけです。

この現象は測定されています。コード生成の標準的なベンチマークである HumanEval に対しテストケースを大幅に増やした EvalPlus では、多くのモデルの合格率が十数ポイント下落しました（Liu et al., 2023）。元のテストが $\alpha > 0$ の検証器だったという直接の証拠です。ベンチマークの合格率をそのままモデルの $p$ と読んではいけません。
</Remark>

### 7.1. 見抜くための具体的な手続き

$\alpha$ を下げるには、次の五つが実際に効きます。

1. **主張を実行可能な形に落とす。** 「この関数は空リストでも動きます」という説明を読んで納得しない。空リストを渡すコードを 1 行書く。LLM の自然言語による説明は、それ自体は検証されていない出力です。
2. **存在するかを一次資料で確かめる。** ライブラリの関数名、引数、戻り値の型は、公式ドキュメントか型定義ファイルで確認します。もっともらしい誤りの中で最も多いのは、実在する命名規則に沿って作られた実在しない API です。エディタの補完が効かないなら、それは存在しない可能性が高い。
3. **退化ケースを自分で列挙する。** 空、1 要素、重複、最大値、負、ゼロ除算、`null`、極端に大きい入力、同時アクセス。この列挙を LLM に任せてはいけません。<Ref to="rem-independence" /> のとおり、検証器は検証対象と独立でなければならないからです。
4. **差分を 1 行ずつ読む。** 自分が書いていない行こそ丁寧に読む必要がありますが、実際には逆になりがちです。読む量を減らすには、<Ref to="thm-decomposition" /> のとおり工程を小さく切ることです。
5. **説明とコードの不一致を疑う。** コメントには正しい仕様が書いてあるのに実装がそれと違う、というパターンは頻出します。コメントは「モデルが何をしようとしたか」の証拠であって、「何をしたか」の証拠ではありません。

次の例は、これらの手続きがなぜ必要かを一つのコードで示します。

<Example id="ex-variance" title="テストを通るのに答えが 3 倍ずれるコード">
「数値のリストの分散（母分散）を返す関数を書いてください」と依頼すると、次のようなコードがよく返ってきます。

```python
def variance(xs):
    n = len(xs)
    s1 = sum(xs)
    s2 = sum(x * x for x in xs)
    return s2 / n - (s1 / n) ** 2
```

$\operatorname{Var}(X) = \mathbb{E}[X^2] - (\mathbb{E}[X])^2$ という公式そのままで、数学的には正しい式です。素朴なテストも通ります。`variance([1, 2, 3, 4])` は $\mathbb{E}[X^2] = 30/4 = 7.5$、$(\mathbb{E}[X])^2 = 2.5^2 = 6.25$ より $1.25$ を返し、これは正解です。

ところが `xs = [1e8, 1e8 + 1, 1e8 + 2]` を渡すと、真の母分散は
$$
\frac{(-1)^2 + 0^2 + 1^2}{3} = \frac{2}{3} \approx 0.6667
$$
であるのに対し、IEEE 754 倍精度でこのコードを評価した結果は **$2.0$** になります。真値の 3 倍です。詳しい追跡は Appendix に置きますが、原因は $10^{16}$ 付近の浮動小数点数の間隔が $2$ もあるため、$s_2/n$ と $(s_1/n)^2$ という**ほとんど等しい二つの巨大な数の差**を取る段階で、有効数字が丸ごと消えることにあります（桁落ち）。

このコードは、構文も型も通り、公式も合っており、説明も自然で、代表的なテストも通ります。まさに <Ref to="def-plausible-error" /> の意味でもっともらしい誤りです。検出するには、手続き 3 の「退化ケースを自分で列挙する」を実行して、**平均が大きく分散が小さいデータ**を自分でテストに加えるしかありません。そしてこの列挙は、統計計算の数値的安定性という**事前の知識**がなければ思いつけません。

正しい実装は二パス法（先に平均を求め、偏差の二乗和を取る）か Welford のオンライン法です。二パス法なら
```python
def variance(xs):
    n = len(xs)
    m = sum(xs) / n
    return sum((x - m) ** 2 for x in xs) / n
```
となり、同じ入力に対して $2/3$ を正しく返します。
</Example>

<Ref to="ex-variance" /> が示しているのは、LLM の誤りを見抜く能力が**その分野の知識そのもの**だということです。桁落ちを知らない人にとって、あの 4 行はどこまで読んでも正しいコードです。「AI が書くから知識は要らなくなる」という予想が外れるのはここです。生成が安くなるほど、検証の能力が相対的に価値を持ちます。この帰結を数学の学習という側面から掘り下げたものが [数学を学び直す意義](/computer-science/ai-era/relearning-mathematics) です。

## 8. 演習

<Exercise id="exr-threshold" difficulty="易">
ある機能の実装について、自分で書くと 120 分（$c_h = 120$）、LLM への指示を書くのに 10 分（$c_g = 10$）、生成物の検証に 20 分（$c_v = 20$）かかると見積もりました。

1. 一度だけ試して駄目なら自分で書く、という戦略が得になるための成功率 $p$ の条件を求めてください。
2. $p = 0.5$ のとき、この戦略の期待コストを求めてください。
3. $p = 0.5$ のとき、合格するまで再生成を続ける戦略の期待コスト（試行回数に上限を設けない場合）を求めてください。

<Solution>
**1.** <Ref to="def-delegation-cost" /> より
$$
g + v = \frac{c_g + c_v}{c_h} = \frac{10 + 20}{120} = \frac{30}{120} = 0.25 .
$$
<Ref to="prop-threshold" /> より、条件は $p > 0.25$ です。成功率が 4 回に 1 回を上回れば委譲が得になります。

**2.** <Ref to="prop-threshold" /> の式に代入します。
$$
C_1 = c_g + c_v + (1-p)c_h = 10 + 20 + 0.5 \times 120 = 30 + 60 = 90 \ \text{分}.
$$
自作の 120 分より 30 分安い。

**3.** <Ref to="prop-retry" /> の $k \to \infty$ の極限より
$$
\frac{c_g + c_v}{p} = \frac{30}{0.5} = 60 \ \text{分}.
$$
期待試行回数が $1/p = 2$ 回なので、$30 \times 2 = 60$ 分と考えても同じです。$p = 0.25$ という閾値を上回っているので、<Ref to="prop-retry" /> のとおり試行を増やすほど期待コストが下がっています。
</Solution>
</Exercise>

<Exercise id="exr-alpha" difficulty="標準">
成功率 $p = 0.6$ の生成に対し、偽陽性率 $\alpha = 0.25$ の検証器（穴のあるテストスイート）を使い、合格するまで再生成して最初に合格したものを採用します。

1. 採用した成果物が誤っている確率を求めてください。
2. この確率を $5\%$ 以下に抑えるには、$\alpha$ をいくつ以下にすればよいですか。

<Solution>
**1.** <Ref to="thm-acceptance-error" /> の式に $p = 0.6$、$\alpha = 0.25$ を代入します。分子は $(1-p)\alpha = 0.4 \times 0.25 = 0.1$、分母は $p + (1-p)\alpha = 0.6 + 0.1 = 0.7$ なので
$$
P_{\text{err}} = \frac{0.1}{0.7} = \frac{1}{7} \approx 0.143 .
$$
約 $14.3\%$ です。7 回に 1 回、誤ったものを本番に通すことになります。

**2.** $P_{\text{err}} \le 0.05$ を $\alpha$ について解きます。分母は正なので、不等号の向きを保ったまま払えます。
$$
\begin{aligned}
\frac{0.4\alpha}{0.6 + 0.4\alpha} \le 0.05
&\iff 0.4\alpha \le 0.05(0.6 + 0.4\alpha) \\
&\iff 0.4\alpha \le 0.03 + 0.02\alpha \\
&\iff 0.38\alpha \le 0.03 \\
&\iff \alpha \le \frac{0.03}{0.38} \approx 0.0789 .
\end{aligned}
$$
偽陽性率を約 $7.9\%$ 以下、つまり「誤った実装を 13 回に 12 回は落とせる」水準までテストを強化する必要があります。<Ref to="thm-acceptance-error" /> の近似式 $P_{\text{err}} \approx \frac{1-p}{p}\alpha = \frac{2}{3}\alpha$ を使うと $\alpha \le 0.075$ が得られ、厳密解に近い値になります。
</Solution>
</Exercise>

<Exercise id="exr-imperfect-split" difficulty="難">
<Ref to="thm-decomposition" /> の逐次方式で、各工程の検証器が完全ではなく、偽陽性率 $\beta \in [0,1)$（偽陰性率 $0$）を持つ場合を考えます。工程正答率を $r$、工程数を $n$、各工程の生成と検証は独立とします。各工程では合格が出るまで生成を繰り返し、最初に合格した出力を採用して次へ進みます。

1. 最終成果物（$n$ 工程すべて）が正しい確率を $r$、$\beta$、$n$ で表してください。
2. $r = 0.98$、$\beta = 0.2$、$n = 50$ のときの値を求めてください。
3. 得られた結果から、工程分割についてどのような実務的な結論が導かれますか。

<Solution>
**1.** 一つの工程に <Ref to="thm-acceptance-error" /> を適用します。同定理で $p \to r$、$\alpha \to \beta$ と置き換えると、その工程で採用された出力が誤りである確率は
$$
\frac{(1-r)\beta}{r + (1-r)\beta}
$$
です。よって正しい確率は
$$
\rho = 1 - \frac{(1-r)\beta}{r + (1-r)\beta} = \frac{r}{r + (1-r)\beta}
$$
となります。工程どうしは独立なので、$n$ 工程すべてが正しい確率は
$$
\rho^{\,n} = \left(\frac{r}{r + (1-r)\beta}\right)^{n} .
$$

**2.** 数値を入れます。$(1-r)\beta = 0.02 \times 0.2 = 0.004$、分母は $0.98 + 0.004 = 0.984$ なので
$$
\rho = \frac{0.98}{0.984} = 0.995935 .
$$
$\ln \rho = -0.0040733$ より
$$
\rho^{50} = e^{50 \times (-0.0040733)} = e^{-0.20366} \approx 0.816 .
$$
約 $81.6\%$ です。裏返すと、約 $18.4\%$ の確率でどこかの工程に誤りが残ったまま完成します。

**3.** <Ref to="thm-decomposition" /> の分割は**期待コスト**を指数的に改善しますが、**正しさ**は工程数に対して指数的に劣化します（$\rho < 1$ なので $\rho^n$ は $n$ について減少）。分割は検証の穴を消すのではなく、穴を通り抜ける機会を $n$ 回に増やすからです。

したがって、工程を細かく切るなら同時に一工程あたりの $\beta$ を下げなければなりません。$n = 50$ で最終正答率を $95\%$ 以上にするには $\rho \ge 0.95^{1/50} = e^{-0.001026} = 0.998975$、すなわち $0.98 / (0.98 + 0.02\beta) \ge 0.998975$ から $\beta \le 0.05$ 程度が要求されます。「小さく切って渡す」という助言は、「切った各断面に検証を置く」という条件とセットでのみ成立します。
</Solution>
</Exercise>

## 参考文献

- Frederick P. Brooks, Jr., "No Silver Bullet: Essence and Accidents of Software Engineering", *IEEE Computer* 20(4) (1987), 10–19. 本質的複雑性と偶有的複雑性の区別。この記事の $v$ が高いタスクは、おおむね本質的複雑性が支配する領域に対応します。
- Brian W. Kernighan and P. J. Plauger, *The Elements of Programming Style*, 2nd ed., McGraw-Hill, 1978. 「デバッグはコードを書くことの 2 倍難しい」という有名な指摘の出典。検証比 $v$ の大きさに関する古典的な観察です。
- Mark Chen et al., "Evaluating Large Language Models Trained on Code", 2021. [arXiv:2107.03374](https://arxiv.org/abs/2107.03374) — HumanEval と pass@k の原論文。
- Jiawei Liu, Chunqiu Steven Xia, Yuyao Wang, Lingming Zhang, "Is Your Code Generated by ChatGPT Really Correct? Rigorous Evaluation of Large Language Models for Code Synthesis", *NeurIPS* 2023. [arXiv:2305.01210](https://arxiv.org/abs/2305.01210) — EvalPlus。テストを強化すると合格率が下がることを示した研究。
- Neil Perry, Megha Srivastava, Deepak Kumar, Dan Boneh, "Do Users Write More Insecure Code with AI Assistants?", *ACM CCS* 2023. [arXiv:2211.03622](https://arxiv.org/abs/2211.03622)
- Sida Peng, Eirini Kalliamvakou, Peter Cihon, Mert Demirer, "The Impact of AI on Developer Productivity: Evidence from GitHub Copilot", 2023. [arXiv:2302.06590](https://arxiv.org/abs/2302.06590) — 対象は「JavaScript で HTTP サーバーを実装する」という単一の、仕様が明確なタスクです。
- METR, "Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity", 2025 — 被験者は自分が保守している大規模リポジトリで作業しており、$c_h$ が小さく（熟知しているので速い）$c_v$ が大きい（品質基準が高い）状況に当たります。
- Nicholas J. Higham, *Accuracy and Stability of Numerical Algorithms*, 2nd ed., SIAM, 2002 — 分散計算の数値的安定性は第 1 章で扱われています。<Ref to="ex-variance" /> の背景。

## Appendix: 桁落ちの数値追跡

**<Ref to="ex-variance" /> の返り値が $2.0$ になる理由を、IEEE 754 倍精度で追跡します。** 倍精度は仮数部が 53 ビットなので、$2^{53} = 9007199254740992 \approx 9.007 \times 10^{15}$ を超えると整数を 1 刻みでは表せません。区間 $[2^{53}, 2^{54})$ での刻み幅（ulp）は $2$、区間 $[2^{54}, 2^{55})$ では $4$ です。

入力は $x_1 = 10^8$、$x_2 = 10^8+1$、$x_3 = 10^8+2$、$n = 3$ です。これらは小さいので厳密に表せます。

- $s_1 = 300000003$、$s_1/n = 100000001$。ともに厳密。
- $(s_1/n)^2$ の真値は $10000000200000001$。これは $[2^{53}, 2^{54})$ に入り刻み幅は $2$、値は奇数なので $10000000200000000$ と $10000000200000002$ のちょうど中間です。偶数丸め（最近接偶数への丸め）の規則により、仮数部の最下位ビットが $0$ になる $10000000200000000$ が選ばれます。
- $s_2$ の各項は $10000000000000000$、$10000000200000001 \to 10000000200000000$（同じ理由）、$10000000400000004$（偶数なので厳密）。左から順に足すと $20000000200000000$、続いて $30000000600000004$ となり、いずれも刻み幅の倍数なので丸め誤差は入りません。
- $s_2 / n = 30000000600000004 / 3 = 10000000200000001.333\ldots$。刻み幅 $2$ の候補 $10000000200000000$（距離 $1.333$）と $10000000200000002$（距離 $0.667$）のうち近いほうが選ばれ、$10000000200000002$ となります。

最後の引き算は $10000000200000002 - 10000000200000000 = 2.0$ で、この減算自体には誤差がありません。すなわち返り値は厳密に $2.0$、真値 $2/3$ の 3 倍です。**誤差が入ったのは引き算ではなく、その前の二つの丸めです。** 引き算はその誤差を露わにしただけで、これが桁落ちという現象の本質です。

**この誤差は入力の大きさとともに悪化します。** 平均を $\mu$、標準偏差を $\sigma$ とすると、$\mathbb{E}[X^2] \approx \mu^2$ と $(\mathbb{E}[X])^2 \approx \mu^2$ の差 $\sigma^2$ を取る計算なので、相対的に失われる桁数はおよそ $\log_{10}(\mu^2/\sigma^2) = 2\log_{10}(\mu/\sigma)$ 桁です。$\mu/\sigma$ が $10^8$ 程度なら 16 桁で、倍精度の有効桁数を丸ごと食い潰します。<Ref to="ex-variance" /> はまさにこの状況でした。

## Appendix: pass@k とこの記事のモデルの関係

**コード生成モデルの論文でよく見る pass@k は、この記事の枠組みで読み替えられます。** pass@k は「$k$ 個のサンプルのうち少なくとも 1 つがテストを通る確率」（<Ref to="computer-science/ai-era/engineer-survival-strategy#def-passk" text="pass@k" />）で、各サンプルが独立に確率 $p$ で通るなら $1 - (1-p)^k$ です。これは <Ref to="prop-retry" /> の「$k$ 回以内に合格する確率」と同じ量です。

注意点が二つあります。第一に、pass@k は**コストを数えていません**。$k$ を増やせば合格確率は上がりますが、そのぶん検証の回数も増えます（<Ref to="computer-science/ai-era/engineer-survival-strategy#ex-passk" text="pass@k は上がるが、検証回数も上がる" />）。実務で効くのは合格確率ではなく期待コストで、そちらは $g+v$ という閾値に支配されます（<Ref to="prop-retry" />）。第二に、pass@k のテストスイートは <Ref to="def-verifier" /> の意味で $\alpha > 0$ の検証器です（Liu et al., 2023）。ベンチマークの数値は $p$ の上限であって $p$ そのものではなく、自分のプロジェクトでの実効的な $p$ は自分の検証器で測るしかありません。
