C Sharpと数値解析 - ニュートン法

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

ニュートン法は非線形方程式の数値解法の1つであり、反復計算により方程式の根を効率的に求める手法である。

解析したい関数 の接線を利用して、近似解を逐次的に求めていくことにある。
基本的なアルゴリズムは、現在の近似値 から次の近似値 を計算する漸化式によって表現される。

関数 と、その導関数 を用いて、次式で近似解を更新する。



上式は、現在の近似値 での関数の値を、その点での接線の傾きで除算することにより、x軸と交わる点へとより近い値を得るというものである。
この過程を繰り返すことにより、最終的に方程式の根に収束させる。

ニュートン法が収束するためには、いくつかの条件が必要となる。
例えば、初期値の選択が重要であり、解の近くに位置する適切な値を選ぶ必要がある。
また、関数の導関数が零にならないことや関数が滑らかであること等が求められる。

ニュートン法は、非線形問題の数値解法として活用されている。
特に、解析的に解くことが困難な方程式を数値的に解く場合に使用されることが多い。

※注意
ニュートン法は、常に収束するわけではなく発散する可能性もある。


方程式の根とは

方程式の根 (または解) とは、その方程式を満たす値、つまり関数の値がゼロになるxの値のことである。

となるxの値を根と呼ぶ。

例えば、 という方程式の根は である。
これらの値を方程式に代入すると、等式が成立する。

ニュートン法は、このような根を数値的に効率的に求めるための手法の1つである。


ニュートン法の使用例

f(x) = 0となる場合

以下の例では、 の根を求めている。
の根を見つけることができる。

収束判定のためのトレランスと最大反復回数のパラメータを指定している。
また、ゼロ除算と収束しない場合はエラーとしている。

 using System;
 
 public class Newton
 {
    /// <summary>
    /// ニュートン法を用いて方程式の根を数値的に求める
    /// </summary>
    /// <param name="function">根を求めたい関数</param>
    /// <param name="derivative">関数の導関数</param>
    /// <param name="initialGuess">初期推定値</param>
    /// <param name="tolerance">収束判定のための許容誤差</param>
    /// <param name="maxIterations">最大反復回数</param>
    /// <returns>方程式の根</returns>
    public static double FindRoot(
       Func<double, double> function, 
       Func<double, double> derivative, 
       double initialGuess, 
       double tolerance = 1e-6, 
       int maxIterations = 100)
    {
       // 初期値を設定
       double x = initialGuess;
 
       for (int i = 0; i < maxIterations; i++)
       {
          // 現在のxにおける関数の値を計算
          double fx = function(x);
 
          // 収束判定 : 関数値が許容誤差以下になったら根と判断
          if (Math.Abs(fx) < tolerance) return x;
 
          // 現在のxにおける導関数の値を計算
          double dfx = derivative(x);
 
          // ゼロ除算を防ぐ : 導関数が0の場合は例外を投げる
          if (dfx == 0) throw new Exception("導関数が0のため、計算を続行できません");
 
          // ニュートン法の反復式
          // 現在の近似値から、接線を使って次の近似値を計算
          x = x - fx / dfx;
       }
 
       // 最大反復回数に達しても収束しない場合は例外を投げる
       throw new Exception("最大反復回数に達しましたが、収束しませんでした");
    }


 // 使用例
 
 // 例 : 方程式 x² - 4 = 0 の根を求める
 // 関数 : f(x) = x² - 4
 Func<double, double> equation = x => x * x - 4;
 
 // 導関数 : f'(x) = 2x
 Func<double, double> dydx = x => 2 * x;
 
 try 
 {
    // 初期推定値1.0から根を求める
    double root = FindRoot(equation, dydx, 1.0);
    Console.WriteLine($"根は次の通りです:{root}");
 }
 catch (Exception ex)
 {
    Console.WriteLine($"エラー:{ex.Message}");
 }


以下の例では、関数 に対して、ニュートン法を適用している。
関数の極値点 (接線の傾きがゼロになる点) を数値的に求めることができる。

 using System;
 
 public class NonlinearEquationSolver
 {
    /// <summary>
    /// ニュートン法による数値的な根 (極値点) 探索
    /// </summary>
    /// <param name="function">解析対象の関数</param>
    /// <param name="derivative">関数の導関数</param>
    /// <param name="initialGuess">初期推定値</param>
    /// <param name="tolerance">収束判定のための許容誤差</param>
    /// <param name="maxIterations">最大反復計算回数</param>
    /// <returns>関数の根または極値点</returns>
    public static double FindRoot(
       Func<double, double> function, 
       Func<double, double> derivative, 
       double initialGuess, 
       double tolerance = 1e-6, 
       int maxIterations = 100)
    {
       // 初期推定値を設定
       double x = initialGuess;
 
       // 指定された最大反復回数まで計算を繰り返す
       for (int i = 0; i < maxIterations; i++)
       {
          // 現在のxにおける関数値を計算
          double fx = function(x);
 
          // 現在のxにおける導関数の値を計算
          double dfx = derivative(x);
 
          // 関数値の絶対値が許容誤差未満の場合に収束と判断
          if (Math.Abs(fx) < tolerance) return x;
 
          // ゼロ除算を防ぐ : 導関数が0の場合は例外を投げる
          if (dfx == 0) throw new Exception("導関数が0のため、計算を続行できません");
 
          // ニュートン法の反復式
          // 接線を利用して次の近似値を計算
          x = x - fx / dfx;
       }
 
       // 最大反復回数に達しても収束しない場合は例外を投げる
       throw new Exception("最大反復回数に達しましたが、収束しませんでした");
    }
 }


 // 使用例
 
 // 例 : 3次関数 f(x) = x³ - 3x² + 2 の極値点を探索
 // この関数はx軸と直接交差しない関数の例
 Func<double, double> f = x => x * x * x - 3 * x * x + 2;
 
 // 上記関数の導関数 : f'(x) = 3x² - 6x
 Func<double, double> df = x => 3 * x * x - 6 * x;
 
 // 異なる初期推定値で根を探索
 double[] initialGuesses = { 0, 1, 2 };
 
 foreach (var guess in initialGuesses)
 {
    try 
    {
       double root = NonlinearEquationSolver.FindRoot(f, df, guess);
       Console.WriteLine($"初期推定値: {guess}, 根: {root}, f(root): {f(root)}");
    }
    catch (Exception ex)
    {
       Console.WriteLine($"エラー: {ex.Message}");
    }
 }


f(x) ≠ 0となる場合

のようなx軸と交差しない実数関数に対しては、標準的なニュートン法は適用できない。
これは、この関数には実数解がないため、ニュートン法の収束が保証されないためである。

そのため、ニュートン法は根を見つけることを前提としているため、適用できない。

複素数領域に拡張すれば、解を見つけることは可能であるが、標準的な実数ベースのニュートン法では対応できない。