数値計算の安定性と条件数
計算結果がなぜ大きく狂うのか、その犯人が問題側かアルゴリズム側かを切り分けられるようになる。条件数と後退安定性、ガウス消去のピボット選択を浮動小数点の観点から正確に解説。
- 1.条件数は問題そのものが持つ感度(入力の相対誤差が出力でどれだけ拡大されるか)で、アルゴリズムを変えても改善しない。
- 2.後退安定なアルゴリズムは「わずかに変えた入力に対する厳密解」を返す。出力誤差はおおよそ 条件数 × マシンイプシロン で抑えられる。
- 3.ガウス消去は部分ピボット選択で増大因子を抑えて後退安定にする。ピボットなしの素朴な消去は丸め誤差が爆発しうる。
「結果が狂う」原因は2つに分かれる
浮動小数点で計算すると、入力にも途中の演算にもごくわずかな丸め誤差が必ず混入します。最終的な誤差が許せないほど大きくなったとき、原因はまったく別の2つに切り分けられます。
- 問題の条件数(conditioning):問題そのものが入力誤差に敏感かどうか。アルゴリズムを変えても改善しない、問題の固有の性質。
- アルゴリズムの安定性(stability):そのアルゴリズムが余計な誤差をどれだけ増幅するか。実装・手順を変えれば改善できる。
この区別を曖昧にすると、悪条件の問題に「もっと安定なアルゴリズム」を投じても無駄に終わるか、逆に良条件の問題で不安定な実装を使って自滅します。丸め誤差の発生源そのものは 浮動小数点数の内部(IEEE 754と誤差) を前提とします。
条件数:問題が持つ感度
条件数は「入力の相対誤差が、出力の相対誤差として何倍に拡大されるか」の上限を表す量です。関数 f を x で評価する問題なら、相対条件数はおおよそ次で定義されます。
条件数 ≒ |出力の相対変化| / |入力の相対変化|
= |x f'(x)| / |f(x)| (1変数の場合)
条件数が1付近なら良条件(well-conditioned)、10^k のオーダーなら出力で約 k 桁の有効数字が失われると見積もれます。これはアルゴリズムを一切含まない、数学的な問題だけの性質である点が肝心です。
ほぼ等しい2値の減算 f(x) = x - c(x が c に近い)は条件数が巨大になります。分子 x f'(x) = x に対し分母 f(x) = x - c がほぼ0だからです。桁落ちは「悪条件な問題を解いている」状態であり、減算を避ける式変形は問題を良条件な等価形に書き換える行為だと理解できます。
連立一次方程式 Ax = b では、行列 A の条件数 cond(A) = ||A|| · ||A^{-1}|| が感度を支配します。cond(A) が大きい行列はほぼ特異(行や列が一次従属に近い)で、b のわずかな誤差が解 x を大きく振らせます。
前進誤差・後退誤差・後退安定性
アルゴリズムの良し悪しを語るには、誤差を2方向で測る必要があります。計算値を ŷ、厳密解を y = f(x) とします。
| 指標 | 意味 | 問う対象 |
|---|---|---|
| 前進誤差 forward error | 計算値と厳密解の差 ||ŷ − y|| | 答えがどれだけ合っているか |
| 後退誤差 backward error | ŷ を厳密解とするような入力の摂動 ||Δx|| | どれだけ違う問題を解いたか |
後退安定(backward stable) なアルゴリズムとは、得られた計算値 ŷ が「わずかに変えた入力 x + Δx に対する厳密解」になっているもの、つまり後退誤差が機械精度オーダーで小さいものを指します。良いアルゴリズムは「正しい答えに近い値」を返すのではなく、「ほぼ正しい入力に対する厳密な答え」を返すのです。
両者は条件数で結びつきます。これが誤差解析の中心的な不等式です。
前進誤差(相対) ≲ 条件数 × 後退誤差(相対)
つまり、後退安定なアルゴリズム(後退誤差 ≒ マシンイプシロン)を使っても、問題が悪条件なら前進誤差は条件数倍まで膨らむ。アルゴリズムは後退誤差しか制御できず、条件数は問題側の定数だからです。
double のマシンイプシロン ε は約 2.2×10^-16。後退安定なアルゴリズムの相対前進誤差は概ね 条件数 × ε で抑えられます。例えば cond(A) が 10^8 なら、解は約8桁失われ、有効桁は16−8=8桁程度しか残らない、と定量的に見積もれます。
ガウス消去とピボット選択
連立一次方程式を解くガウス消去は、後退安定性がピボット選択に左右される典型例です。素朴な消去では、各ステップでピボット(対角要素)で割って下の行を消去しますが、ピボットが小さいと割り算で乗数が巨大化し、丸め誤差を増幅します。
# ピボットが極小だと乗数 m = a[i][k] / a[k][k] が爆発する
m = a[i][k] / a[k][k] # a[k][k] ≈ 0 で m が巨大に
a[i][:] -= m * a[k][:] # 巨大な m を掛けて引くと情報落ちで桁が消える
部分ピボット選択(partial pivoting) は、各列で絶対値が最大の要素を行交換でピボット位置に持ってくる手法です。これにより乗数の絶対値が必ず1以下に収まり、消去で生じる要素の増大(増大因子 growth factor)が抑えられます。
| 方式 | ピボットの選び方 | 後退安定性 |
|---|---|---|
| ピボットなし | 対角要素をそのまま使う | 不安定。極小ピボットで誤差が爆発しうる |
| 部分ピボット選択 | 列内の絶対値最大を行交換で選ぶ | 実用上ほぼ常に安定。標準的な既定 |
| 完全ピボット選択 | 残り全体の絶対値最大を行・列交換で選ぶ | 理論上最も安定だが探索コストが高い |
部分ピボット選択付きガウス消去は、後退誤差が増大因子に比例します。最悪ケースでは増大因子が 2^(n-1) まで理論上膨らみ得ますが、実問題でそうなる行列は極めて稀で、平均的には穏やかに振る舞うため、現実的にはほぼ常に後退安定とみなせます。これが LAPACK など実用ライブラリの既定が部分ピボット選択である理由です。
ピボット選択を「ピボットが0のときだけ行交換する処理」と誤解しがちですが、本質は乗数を小さく保って増大因子を抑えることにあります。ピボットが0でなくても小さければ交換が必要です。数値的な安定性のための操作であり、特異性チェックとは目的が異なります。
誤差の伝播を浮動小数点で捉える
各浮動小数点演算は、丸めにより相対誤差を高々 ε だけ持ち込みます。これを fl(a op b) = (a op b)(1 + δ)、|δ| ≤ ε とモデル化するのが標準的な丸め誤差解析です。
- 1回の演算の相対誤差は ε で頭打ち。問題は演算を重ねたときの累積と増幅。
- 乗算・除算は相対誤差をおおむね保つ(拡大しにくい)。
- 加減算、特に近接値の減算は相対誤差を激しく拡大する(桁落ち=局所的な悪条件)。
n 個の演算を直列に行うと、誤差は素朴には n × ε のオーダーで蓄積します。総和のように演算回数が要素数に比例する処理では、加算順序や Kahan の補償総和で蓄積を抑えるのが定石です。アルゴリズムの演算回数そのものの見積もりは 計算量と Big-O 記法 の領域ですが、演算が多いほど誤差蓄積の余地も増える点で安定性と無関係ではありません。
# 同じ和でも、足す順序や補償の有無で結果が変わる
import math
xs = [1e16, 1.0, -1e16]
acc = 0.0
for x in xs: # 素朴な左からの逐次加算
acc += x # 1e16 + 1.0 が 1e16 に丸められ 1.0 が吸収される
print(acc) # → 0.0(情報落ちで 1.0 が消える)
print(math.fsum(xs)) # → 1.0(誤差補償付き総和で正しい)
# 注: CPython 3.12+ の組み込み sum() は浮動小数点に補償加算を使うため
# sum(xs) はこの例では 1.0 を返す。素朴な逐次加算とは挙動が異なる。
このように、同じ数学的に等価な式でも、評価順序や式の形で前進誤差が桁違いに変わります。これはアルゴリズム(安定性)側の問題であり、式変形で改善できます。一方、行列 A が悪条件であること自体は変形では消せません。型の選択や精度の意味は 変数とデータ型 も参照してください。
「結果が不正確」と言われたら、まず条件数(問題側)とアルゴリズムの安定性(実装側)を分けて診断する。悪条件なら高精度型(倍々精度・任意精度)や問題の再定式化が必要で、不安定なだけならピボット選択や式変形・補償総和で解決する。両者は対処法が根本的に異なる。
まとめ
- 条件数は問題固有の感度(入力相対誤差の増幅率)で、アルゴリズムでは改善できない。
cond(A)が10^kなら約 k 桁失われる。 - 後退安定なアルゴリズムは「わずかに摂動した入力に対する厳密解」を返す。前進誤差は概ね
条件数 × マシンイプシロンで抑えられる。 - ガウス消去は部分ピボット選択で乗数と増大因子を抑え、後退安定にする。素朴な消去は極小ピボットで誤差が爆発しうる。
- 加減算(特に近接値の減算)が誤差を拡大し、乗除算は保つ。蓄積は演算順序・補償総和で緩和できる。
- 診断は「問題が悪条件か、アルゴリズムが不安定か」の切り分けから始める。対処法は両者で異なる。
プログラミング Article
数値計算の安定性と条件数を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
数値計算
比較で見る軸
難易度: advanced / カテゴリ: プログラミング / タグ数: 6
導入後に効く点
後退安定なアルゴリズムは「わずかに変えた入力に対する厳密解」を返す。出力誤差はおおよそ 条件数 × マシンイプシロン で抑えられる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- プログラミング
- タグ数
- 6
判断チェックリスト
- 自社の用途が「数値計算 / 条件数」に近いか確認する。
- 強みである「条件数は問題そのものが持つ感度(入力の相対誤差が出力でどれだけ拡大されるか)で、アルゴリズムを変えても改善しない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。