Root Finding — 求根アルゴリズム

概要

sangi の求根モジュールは 3 つのカテゴリに分かれる。

  • 1D 求根 — $f(x) = 0$ を満たす実数 $x$ を求める。二分法から Brent 法まで 10+ 種類
  • 多項式求根 — $p(x) = 0$ の全根 (複素根含む) を求める。低次は公式解、高次は反復法
  • N-D 求根 — $\mathbf{F}(\mathbf{x}) = \mathbf{0}$ を満たすベクトル $\mathbf{x}$ を求める

結果型と収束判定

RootFindingResult<T>

1D 求根関数および多項式根反復関数の戻り値型。 math/roots/root_finding_base.hpp 定義。

template<typename T>
struct RootFindingResult {
    std::optional<T> root;     // 成功時: 根の近似値、失敗時: std::nullopt
    bool             converged; // 収束したかどうか
    size_t           iterations;// 実際に行った反復回数
    T                error_estimate; // 推定誤差 (絶対値、収束時は最終ステップ幅 等)
};
メンバ意味
rootstd::optional<T>収束した場合の根の近似値。 失敗時は std::nullopt
convergedbool収束判定が満たされたか。 部分成功 (max iter 到達だが値はそれなり) でも true になる場合あり
iterationssize_t実行した反復回数 (収束したステップ数、 または上限到達時の max_iterations)
error_estimateT残差・ステップ幅・区間幅 等で推定した最終誤差の絶対値

使い方:

auto r = brent_method(f, a, b);
if (r.converged && r.root) {
    double x = *r.root;            // または r.root.value()
    // ... x を使う
} else {
    // 失敗 (区間に符号変化なし / max iter 到達 / 数値特異 等)
    std::cerr << "no root: iter=" << r.iterations
              << " err=" << r.error_estimate << '\n';
}

N-D 求根関数は同じ構造の SystemRootFindingResult<V, T> を返す (root がベクトル型 std::optional<V> である点だけ異なる)。

ConvergenceCriteria<T>

収束判定の閾値を保持する struct。 全求根関数の最後の引数として渡せる (省略時はデフォルト)。

template<concepts::OrderedField T>
struct ConvergenceCriteria {
    T      abs_ftol = std::numeric_limits<T>::epsilon() * 100;   // |f(x)| 絶対許容誤差
    T      abs_xtol = std::numeric_limits<T>::epsilon() * 100;   // |Δx|   絶対許容誤差
    T      rel_ftol = std::numeric_limits<T>::epsilon() * 1000;  // |f|相対許容誤差
    T      rel_xtol = std::numeric_limits<T>::epsilon() * 1000;  // Δx 相対許容誤差
    size_t max_iterations = 100;                                  // 反復上限
};
メンバ意味デフォルト (T=double)
abs_ftol$|f(x)|$ がこれ以下なら収束 (絶対基準)$\approx 2.2 \times 10^{-14}$
abs_xtol$|\Delta x|$ や区間幅 $|b-a|$ がこれ以下なら収束$\approx 2.2 \times 10^{-14}$
rel_ftol$|f_n - f_{n-1}| / |f_{n-1}|$ がこれ以下なら収束$\approx 2.2 \times 10^{-13}$
rel_xtol$|x_n - x_{n-1}| / \max(1, |x_{n-1}|)$ がこれ以下なら収束$\approx 2.2 \times 10^{-13}$
max_iterations反復回数の上限。 超えると収束失敗扱い100

使用例:

// 緩い許容誤差で素早く打ち切る
ConvergenceCriteria<double> loose;
loose.abs_ftol = 1e-6;
loose.max_iterations = 50;
auto r = newton_raphson(f, df, x0, loose);

// 多倍長 (Float) で高精度
ConvergenceCriteria<Float> tight;
tight.abs_ftol = Float("1e-50");
tight.max_iterations = 200;
auto r2 = brent_method<Float>(f, a, b, tight);

1D: 囲い込み法 (Bracketing)

区間 $[a, b]$ 内で $f(a)$ と $f(b)$ が異符号であることを前提とし、確実に収束する。

関数収束次数説明
bisection(f, a, b)線形二分法。最も単純で確実
false_position(f, a, b)超線形偽位置法 (Regula Falsi)
ridders_method(f, a, b)$\sqrt{2}$Ridders 法。二分法より速い
brent_method(f, a, b)超線形Brent 法。実用上の最良選択
toms748(f, a, b)$\approx 1.84$TOMS Algorithm 748。Brent の改良版

関数詳細

bisection

template<concepts::OrderedField T>
RootFindingResult<T> bisection(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 区間 $[a, b]$ を毎ステップ半分に絞り、$f$ の符号が変わる側を残す。 線形収束 (毎反復で誤差が 1/2)。

パラメータ: f 1 変数実関数 / a, b 初期区間端点 ($f(a) \cdot f(b) < 0$ が必須) / criteria 収束判定。

返り値: RootFindingResult<T>。 区間に符号変化がない場合は converged = falseroot = std::nullopt

用途: ロバスト性が最優先で速度は二の次のとき。 区間幅が確実に半減するため最悪反復回数を予測可能 (log2((b-a)/tol))。

auto r = bisection<double>([](double x){ return x*x - 2.0; }, 1.0, 2.0);
if (r.converged) std::cout << *r.root;
// 実行結果 (build & run 検証済): iter=42  root=1.41421356237311
//   ※ 真値 sqrt(2) = 1.41421356237310 と 12 桁一致

false_position

template<concepts::OrderedField T>
RootFindingResult<T> false_position(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 区間端点を結ぶ直線と $x$ 軸の交点を次の試行値とする。 凹な関数では一方の端点が動かず線形になる弱点がある (Illinois / Anderson-Björk 等の改良法はここでは未実装)。

パラメータ: bisection と同じ。 $f(a) \cdot f(b) < 0$ が必須

ridders_method

template<concepts::OrderedField T>
RootFindingResult<T> ridders_method(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 区間の中点 $m$ と $e^{Q(x-m)}$ で重み付けした false position を組合せる。 1 反復で $f$ を 2 回評価する代わりに $\sqrt{2} \approx 1.414$ 次の超線形収束を得る (実効収束次数は約 1.84)。

用途: bisection より速く、 Brent より実装が単純。 関数評価が安価な場合の中庸選択。

brent_method

template<concepts::OrderedField T>
RootFindingResult<T> brent_method(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 逆 2 次補間 / 割線法 / 二分法を内部状態に応じて切替える Brent (1973) のハイブリッド。 区間幅が縮まないステップは bisection で強制縮退するため、 速度と保証収束を両立する。

用途: 1D 求根のデフォルト推奨。 SciPy brentq 等の標準アルゴリズム。 区間端点で符号が異なれば常に確実に根を返す。

auto r = brent_method<double>([](double x){ return std::cos(x) - x; }, 0.0, 1.0);
if (r.converged) std::cout << *r.root;
// 実行結果 (build & run 検証済): iter=26  root=0.739085133215161
//   ※ Dottie 数 = 0.7390851332151607 と 14 桁一致

toms748

template<concepts::OrderedField T>
RootFindingResult<T> toms748(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: Alefeld-Potra-Shi (1995) の TOMS Algorithm 748。 逆 3 次補間と double-length step を組合せて Brent より高い実効収束次数 ($\approx 1.84$ for smooth $f$) を持つ。

用途: Brent と同じく区間保証付きで、 滑らかな $f$ では Brent より少ない反復で収束。 SciPy scipy.optimize.brentq の代替に推奨。

1D: 非区間法 (Non-bracketing)

初期値 $x_0$ (と任意で導関数) から反復する。収束は速いが初期値依存。

関数収束次数説明
newton_raphson(f, f', x0)2 次Newton-Raphson 法。 導関数が必要
secant_method(f, x0, x1)$\approx 1.618$割線法。 導関数不要 (2 点から有限差分近似)

関数詳細

newton_raphson

template<concepts::OrderedField T>
RootFindingResult<T> newton_raphson(
    const std::function<T(T)>& f,
    const std::function<T(T)>& df,
    T x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 各反復 $x_{n+1} = x_n - f(x_n) / f'(x_n)$。 単根近傍で 2 次収束 ($|x_{n+1} - x^*| \le C |x_n - x^*|^2$)。 重根では線形に劣化。

パラメータ: f 関数 / df 導関数 / x0 初期値 / criteria 収束判定。

注意: $|f'(x)| < \epsilon \cdot 10$ の near-stationary 点で発散判定 → converged=false。 初期値が悪いと別の根に収束、 または発散することがある。 区間保証がほしい場合は brent_method を推奨。

// 例: f(x) = x^2 - 2 の根を Newton 法で求める
auto r = newton_raphson<double>(
    [](double x) { return x*x - 2.0; },
    [](double x) { return 2.0 * x; },
    1.0);
if (r.converged) std::cout << *r.root;
// 実行結果 (build & run 検証済): iter=5  root=1.41421356237310
//   ※ 真値 sqrt(2) = 1.41421356237310 と 15 桁一致 (5 反復で収束、 2 次収束の威力)

secant_method

template<concepts::OrderedField T>
RootFindingResult<T> secant_method(
    const std::function<T(T)>& f,
    T x0, T x1,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 2 点 $(x_{n-1}, x_n)$ を結ぶ割線 (secant) と $x$ 軸の交点を次の試行値: $x_{n+1} = x_n - f(x_n) \frac{x_n - x_{n-1}}{f(x_n) - f(x_{n-1})}$。 導関数を 1 次差分で代用するため Newton よりわずかに遅い ($\approx 1.618$ 次収束、 黄金比)。

パラメータ: f 関数 / x0, x1 初期 2 点 / criteria 収束判定。 $x_0, x_1$ は近すぎず、 根を挟む必要はない。

注意: $|f(x_n) - f(x_{n-1})| < \epsilon$ (傾きほぼゼロ) で停滞検出 → converged=false

// 例: f(x) = x - cos(x) の根 (= Dottie 数)
auto r = secant_method<double>(
    [](double x) { return x - std::cos(x); }, 0.0, 1.0);
if (r.converged) std::cout << *r.root;
// 実行結果 (build & run 検証済): iter=6  root=0.739085133215161
//   ※ Dottie 数 0.7390851332151607 と 14 桁一致

1D: 高次収束法

関数収束次数説明
halley_method(f, f', f'', x0)3 次Halley 法。 1 階・2 階導関数が必要
schroder_method(f, f', f'', x0)2 次 (重根含む)Schröder 法 (Householder 2 次)。 重根でも 2 次収束を維持
king_method(f, a, b)超線形King-Pegasus 法 (1973、 BIT)。 区間 $[a,b]$ を取る bracketing 法。 Regula Falsi の改良で Illinois より速い
popovski_method(f, f', x0)4 次Popovski 法。 1 階導関数のみで 4 次収束

関数詳細

halley_method

template<concepts::OrderedField T>
RootFindingResult<T> halley_method(
    const std::function<T(T)>& f,
    const std::function<T(T)>& df,
    const std::function<T(T)>& d2f,
    T x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 更新式 $x_{n+1} = x_n - \frac{2 f f'}{2 f'^2 - f f''}$。 Newton の 2 次収束を 3 次に引き上げる Householder 1 次。 2 階導関数が必要、 計算コストは Newton の約 2 倍だが反復回数は減る。

パラメータ: f, df, d2f 関数・1 階・2 階導関数 / x0 初期値 / criteria 収束判定。

注意: 分母 $2 f'^2 - f f''$ がほぼゼロで発散判定。

// 例: f(x) = x^3 - 2 の根 (= 2^(1/3))
auto r = halley_method<double>(
    [](double x) { return x*x*x - 2.0; },
    [](double x) { return 3.0 * x * x; },
    [](double x) { return 6.0 * x; },
    1.0);
if (r.converged) std::cout << *r.root;
// 実行結果 (build & run 検証済): iter=3  root=1.25992104989487
//   ※ 真値 2^(1/3) = 1.2599210498948732 と 14 桁一致 (3 反復、 3 次収束の威力)

schroder_method

template<concepts::OrderedField T>
RootFindingResult<T> schroder_method(
    const std::function<T(T)>& f,
    const std::function<T(T)>& df,
    const std::function<T(T)>& d2f,
    T x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 更新式 $x_{n+1} = x_n - \frac{f f'}{f'^2 - f f''}$。 重根に対して通常の Newton が 1 次に劣化するのに対し、 Schröder (1870) は 重根でも 2 次収束を維持する設計。

用途: 多項式の重根を扱う場合、 Halley より安定。 単根では Halley (3 次) の方が速い。

king_method

template<concepts::OrderedField T>
RootFindingResult<T> king_method(
    const std::function<T(T)>& f, T a, T b,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: King-Pegasus 法 (R.F.King, BIT 1973)。 Regula Falsi で補間点を求めた後、 同じ端点が連続停滞した場合に $f_1 \leftarrow f_1 \cdot f_2 / (f_2 + f)$ で修正して再補間。 Illinois 法より速い超線形収束。

パラメータ: f 関数 / a, b 端点 ($f(a) \cdot f(b) < 0$ 必須) / criteria 収束判定。

popovski_method

template<concepts::OrderedField T>
RootFindingResult<T> popovski_method(
    const std::function<T(T)>& f,
    const std::function<T(T)>& df,
    T x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 2 ステップ更新 (Newton ステップ + 補正項) で 1 階導関数のみから 4 次収束を実現。 King-Werner / Ostrowski 系の改良。 2 階導関数を計算しない代わり 1 反復あたり $f$ を 2 回評価する。

用途: 2 階導関数が高コスト (e.g. AD で計算) なケースで、 反復回数より関数評価回数の方が問題な時。

多項式求根

全関数は std::vector<Complex<T>> を返す (実根は虚部 = 0)。 根はベクトル空間の元ではなく単なる複素数の集合なので、 sangi::Vector (線形代数用) ではなく STL の std::vector を使う。 入力 Polynomial<T> は係数を 昇べき順で保持 (p[0] = 定数項)。

低次 (公式解)

関数説明
solveLinear(p)1 次: $ax + b = 0$
solveQuadratic(p)2 次: モニック化 + 大きい絶対値の根を先に求めて Vieta で小さい根 (桁落ち対策)
solveCubic(p)3 次: Cardano の公式 + 判別式 < 0 の場合は三角関数法
solveQuartic(p)4 次: Ferrari の方法 (解決三次方程式 + 2 つの二次)

高次 (反復法)

関数説明
jenkinsTraub(p)Jenkins-Traub 三段階法。 数値的に最も安定、 5-19 次の標準推奨
laguerre(p, eps, maxIter)Laguerre 法 + デフレーション。 3 次収束、 全根に大域収束保証
durandKernerAberth(p, eps, maxIter)Durand-Kerner-Aberth + Newton polish。 全根同時反復、 20 次以上の推奨
bairstow(p, eps, maxIter)Bairstow 法。 実係数のみで 2 次因子抽出、 複素根は共役ペアとして自然に出る

関数詳細

solveLinear / solveQuadratic / solveCubic / solveQuartic

template<typename T> std::vector<Complex<T>> solveLinear(const Polynomial<T>& p);
template<typename T> std::vector<Complex<T>> solveQuadratic(const Polynomial<T>& p);
template<typename T> std::vector<Complex<T>> solveCubic(const Polynomial<T>& p);
template<typename T> std::vector<Complex<T>> solveQuartic(const Polynomial<T>& p);

動作: それぞれ 1/2/3/4 次方程式に対する閉形式解。 次数不一致は assert で検出。

桁落ち対策: solveQuadratic は判別式が小さい場合に大きい絶対値の根を直接計算し、 もう一方を Vieta の公式で導出 (Numerical Recipes 推奨)。 出典: 奥村「アルゴリズム事典」p205。

// 例: x^2 - 5x + 6 = (x-2)(x-3) = 0
Polynomial<double> p({6.0, -5.0, 1.0});  // 昇べき: c0=6, c1=-5, c2=1
auto roots = solveQuadratic(p);
for (auto& r : roots) std::cout << r.re << ' ';
// 実行結果 (build & run 検証済): "3 2" (真値 {2, 3} と完全一致)

jenkinsTraub

template<typename T>
std::vector<Complex<T>> jenkinsTraub(const Polynomial<T>& poly);

動作: Jenkins-Traub (1970) の RPOLY アルゴリズム (3 段階 shift-shift-shift)。 各根を 1 つずつ求め、 デフレーション (因数除去) で次数を下げる。 同分野で数十年にわたり標準的な「最も信頼できる」多項式求根法。

用途: 中次数 (5-19 次) のデフォルト推奨solvePolynomial も 5-19 次でこれを内部呼出。

// 例: x^6 - 1 = 0 (1 の 6 乗根)
Polynomial<double> p({-1.0, 0, 0, 0, 0, 0, 1.0});
auto roots = jenkinsTraub(p);
// 実行結果 (build & run 検証済): 6 根 = {±1, ±exp(±iπ/3)}
//   |p(root)| 残差最大 4.97e-16 (16 桁精度)

laguerre

template<typename T>
std::vector<Complex<T>> laguerre(
    const Polynomial<T>& poly,
    T eps = std::numeric_limits<T>::epsilon() * T(100),
    size_t maxIter = 1000);

動作: Laguerre 法 + デフレーション。 各反復で $L(z) = -n / \left( G \pm \sqrt{(n-1)(n H - G^2)} \right)$ 更新 ($G = p'/p$, $H = G^2 - p''/p$)。 任意の初期値から全根に大域収束する珍しい性質、 3 次収束。

パラメータ: eps 収束許容誤差 / maxIter 反復上限。

durandKernerAberth

template<typename T>
std::vector<Complex<T>> durandKernerAberth(
    const Polynomial<T>& poly,
    T eps = std::numeric_limits<T>::epsilon() * T(100),
    size_t maxIter = 1000);

動作: Aberth-Ehrlich の全根同時反復 ($z_i \leftarrow z_i - p(z_i)/p'(z_i) / (1 - p(z_i)/p'(z_i) \cdot \sum_{j \ne i} 1/(z_i - z_j))$)。 収束後に元の多項式で各根を Newton polish 精密化する。 全根同時のため デフレーション誤差が蓄積しない

用途: 高次 (20 次以上) のデフォルト推奨solvePolynomial も 20 次以上で内部呼出。

bairstow

template<typename T>
std::vector<Complex<T>> bairstow(
    const Polynomial<T>& poly,
    T eps = std::numeric_limits<T>::epsilon() * T(100),
    size_t maxIter = 1000);

動作: 試行 2 次因子 $x^2 + rx + s$ で多項式を割り、 Newton 法で $r, s$ を修正して 2 次因子を抽出。 実数演算のみで複素根 (共役ペア) を得る。 出典: Press et al., Numerical Recipes §9.5。

用途: 複素数演算を避けたい組込み環境、 実係数のみで複素根の組をペア単位で扱いたい場合。

統合ディスパッチャ

template<typename T>
std::vector<Complex<T>> solvePolynomial(
    const Polynomial<T>& p,
    T eps = std::numeric_limits<T>::epsilon() * T(100),
    size_t maxIter = 1000);

solvePolynomial は次数に応じて自動的に最適なアルゴリズムを選択する:

  • 1 次: solveLinear (閉形式)
  • 2 次: solveQuadratic (閉形式、 桁落ち対策付)
  • 3 次: solveCubic (Cardano + 三角関数)
  • 4 次: solveQuartic (Ferrari)
  • 5-19 次: jenkinsTraub (1 根ずつ + デフレーション、 高精度)
  • 20 次以上: durandKernerAberth (全根同時 + Newton polish、 デフレーション誤差回避)

N-D: 多変数求根

$\mathbf{F}: \mathbb{R}^n \to \mathbb{R}^n$ の零点を求める。 戻り値は SystemRootFindingResult<V, T> (1D の RootFindingResult のベクトル版)。

関数説明
newton_raphson_system(F, J, x0)多変数 Newton 法。 Jacobian 関数が必要
broyden_method(F, x0)Broyden 法。 Jacobian 不要 (準 Newton 法、 ランク 1 更新)
levenberg_marquardt(F, J, x0)Levenberg-Marquardt 法。 ダンピング付き、 初期値が悪くても収束しやすい
powell_hybrid(F, J, x0)Powell のハイブリッド法。 信頼領域 + dogleg

関数詳細

newton_raphson_system

template<concepts::OrderedField T, typename V, typename M>
    requires concepts::VectorOf<V, T> && concepts::MatrixOf<M, T>
SystemRootFindingResult<V, T> newton_raphson_system(
    const std::function<V(const V&)>& F,
    const std::function<M(const V&)>& J,
    const V& x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 各反復で $\mathbf{x}_{n+1} = \mathbf{x}_n - J(\mathbf{x}_n)^{-1} F(\mathbf{x}_n)$。 ヤコビアン特異時は対角要素を abs_xtol * 10 で正則化して solve をリトライ。

パラメータ: F 系を表すベクトル値関数 / J ヤコビアンを返す関数 / x0 初期ベクトル / criteria 収束判定。

返り値: SystemRootFindingResult<V, T>rootstd::optional<V>

収束判定: $\|F(\mathbf{x})\|_2 < \mathrm{abs\_ftol}$ または $\|\Delta\mathbf{x}\|_2$ が許容値以下。

例: 単位円 $x^2 + y^2 = 1$ と直線 $y = x$ の交点を求める。 sangi::Matrix<T>::solve() を経由して concept 要件を満たすため <math/linalg/solvers.hpp> も include する。

#include <math/roots/root_finding.hpp>
#include <math/linalg/solvers.hpp>   // Matrix::solve() の本体定義はここ

using namespace sangi;

auto F = [](const Vector<double>& v) {
    Vector<double> r(2);
    r[0] = v[0]*v[0] + v[1]*v[1] - 1.0;   // x^2 + y^2 - 1
    r[1] = v[0] - v[1];                   // x - y
    return r;
};
auto J = [](const Vector<double>& v) {
    Matrix<double> m(2, 2);
    m(0,0) = 2*v[0]; m(0,1) = 2*v[1];
    m(1,0) = 1.0;    m(1,1) = -1.0;
    return m;
};
Vector<double> x0(2); x0[0] = 0.5; x0[1] = 0.5;
auto r = newton_raphson_system<double, Vector<double>, Matrix<double>>(F, J, x0);
if (r.converged && r.root) {
    const auto& v = *r.root;
    std::cout << v[0] << ", " << v[1];
}
// 実行結果 (build & run 検証済): iter=5  root=(0.707106781186548, 0.707106781186548)
//   ※ 真値 1/√2 = 0.7071067811865476 と 15 桁一致

broyden_method

template<concepts::OrderedField T, typename V, typename M>
SystemRootFindingResult<V, T> broyden_method(
    const std::function<V(const V&)>& F,
    const V& x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: 「Good Broyden」式の準 Newton 法。 ヤコビアンを毎回計算せず、 ランク 1 更新で近似 ($B_{n+1} = B_n + \frac{(\Delta F - B_n \Delta \mathbf{x}) \Delta \mathbf{x}^\top}{\|\Delta \mathbf{x}\|^2}$)。

用途: ヤコビアンが解析的に書けない / 計算コストが高い場合。 初期 $B_0$ は単位行列、 後は更新で改善。

auto r = broyden_method<double>(F, x0);  // J 不要、 F のみ与える

levenberg_marquardt

template<concepts::OrderedField T, typename V, typename M>
SystemRootFindingResult<V, T> levenberg_marquardt(
    const std::function<V(const V&)>& F,
    const std::function<M(const V&)>& J,
    const V& x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: $(J^\top J + \lambda I) \Delta\mathbf{x} = -J^\top F$ を解く。 ダンピングパラメータ $\lambda$ を成功時減らし、 失敗時増やす (Marquardt-Levenberg 更新規則)。 Newton と最急降下の補間。

用途: 初期値が真の根から遠い / ヤコビアンが特異近傍を通る / 最小二乗の感覚で根を探したい場合。 単なる方程式系解法というより、 残差最小化の混成手法。

powell_hybrid

template<concepts::OrderedField T, typename V, typename M>
SystemRootFindingResult<V, T> powell_hybrid(
    const std::function<V(const V&)>& F,
    const std::function<M(const V&)>& J,
    const V& x0,
    const ConvergenceCriteria<T>& criteria = ConvergenceCriteria<T>());

動作: Powell (1970) の dogleg-trust-region。 信頼領域内で Newton 方向と最急降下方向を組合せた dogleg 経路をたどる。 MINPACK hybrd ルーチン (SciPy fsolve の標準) と同系統。

用途: N-D 求根のデフォルト推奨。 局所収束性 (Newton 並み) と大域収束性 (信頼領域) を兼ね備える。

使用例

#include <math/roots/root_finding.hpp>
#include <iostream>
using namespace sangi;

int main() {
    // 1D: f(x) = x^2 - 2 の根を Brent 法で求める
    auto result = brent_method<double>(
        [](double x) { return x * x - 2.0; },
        1.0, 2.0);
    if (result.converged && result.root)
        std::cout << "sqrt(2) = " << *result.root << std::endl;
    // 出力: sqrt(2) = 1.41421...

    // 多項式: x^3 - 6x^2 + 11x - 6 = (x-1)(x-2)(x-3) の全根
    Polynomial<double> p({-6.0, 11.0, -6.0, 1.0});  // 昇べき
    auto roots = solvePolynomial(p);
    for (auto& r : roots)
        std::cout << r.re << std::endl;
    // 出力: 1, 2, 3 (順序は実装依存)

    // 5次以上: solvePolynomial が自動的に jenkinsTraub を呼ぶ
    Polynomial<double> q({1.0, 0.0, 0.0, 0.0, 0.0, 1.0});  // x^5 + 1
    auto roots5 = solvePolynomial(q);
    for (auto& r : roots5)
        std::cout << r.re << " + " << r.im << "i" << std::endl;
}

関連する数学的背景

以下の記事では、Roots モジュールの基盤となる数学的概念を解説している。