【Python】割線法-方程式の数値解とグラフ出力、黄金比収束

サムネイル-Pythonで割線法(セカント法)を表現します。
当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク

割線法(セカント法)は、非線形方程式の数値解法の一つで、
関数の解を近似的な接線(割線)を用いて求める手法です。

主に、ニュートン法が使えない関数の解を求めるときに使用します。

Pythonによるニュートン法を解説しています。

本記事では、ニュートン法と比較しながらPythonを使って二分法を表現します。

Udemyで学習する
スポンサーリンク

割線法とニュートン法の違い

非線形方程式や連立方程式など解析的に解けない方程式は、
ニュートン法のような反復法を用いて、解を求めます。

しかし、ニュートン法は示した漸化式から分かるように、
導関数\(f^{\prime}(x)\)がなければ使えないアルゴリズムです。

$$x_{2}=x_1-\frac{f(x_1)}{f^{\prime}(x_1)}$$

導関数を持たない関数の例
  1. 絶対値関数の原点における導関数\(f(x)=|x|\)
  2. 階段関数\(f(x)=[x]\)

導関数\(f^{\prime}(x)\)が使えないことは、関数\(f(x)\)の接線がないことを意味しています。

そこで、接線の代わりに関数\(f(x_0)\)と\(f(x_1)\)を通る直線を用いて、
\(x\)軸上の\(x_2\)を求める手法が割線法です。

$$f^{\prime}(x_1)\simeq\frac{f(x_1)-f(x_0)}{x_1-x_0}$$

導関数\(f^{\prime}(x_1)\)は接線の式から得た近似式で、
この式をニュートン法の式に代入すると、\(x\)軸上の\(x_2\)は、

$$x_{2}=x_1-\frac{f(x_1)(x_1-x_0)}{f(x_1)-f(x_0)}$$

となります。得た式を一般化すると、次の漸化式になります。

$$x_{n+2}=x_{n+1}-\frac{f(x_{n+1})(x_{n+1}-x_{n})}{f(x_{n+1})-f(x_{n})}$$

割線法(セカント法)の概形

以上までが割線法の基本的なアルゴリズムになります。

割線法の手順
  1. \(x_0 \)および\(x_1 \)の2点を初期値の推定値として設定。
  2. \(f(x_0) \)および\(f(x_1) \)を求めて、漸化式から\(y=0 \)と交わる\(x\)軸上の点\(x_2 \)を設定。
  3. 同様に、\(f(x_2)\)を求めて、漸化式から\(x_3\)を設定。

ニュートン法のように関数\(f(x)\)の導関数を求める必要がないため、計算コストが低いです。

また、初期の推定解を選ぶ柔軟性があります。
これにより、導関数の計算が難しい場合コストを抑えたい場合に有用です。

Pythonで表現

Pythonで割線法を使用して解を表現します。

例題

\(f(x)=x^2-2=0 \)を割線法で解く。

読者の方であれば、解は\(x=\pm\sqrt{ 2 } \)だと分かるかと思いますが、
この値を割線法で考えます。

数値解析で使用頻度が高いライブラリの一覧を確認できます。

ソースコード

def f(x):
    return x**2 - 2

def secant_method(default, precision=1e-8, max_step=100):
    x0 = default
    x1 = default + 0.01  # 少し離れた点を選ぶことで近似的に接線とみなせる。
    step = 0

    while abs(f(x1)) > precision:
        print(f"反復 {step}: x{step} = {x0}, x{step+1} = {x1}, f(x{step}) = {f(x0)}, f(x{step+1}) = {f(x1)}")
        x_temp = x1 - f(x1) * (x1 - x0) / (f(x1) - f(x0))
        x0 = x1
        x1 = x_temp
        step += 1

        if step >= max_step:
            raise RuntimeError("収束しませんでした。")

    print(f"\n方程式の解: {x1}")
    return x1

# 初期値
default = 1.0

# 割線法を実行
result = secant_method(default)

UdemyでPythonを学習

Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。




解説

\(f(x)\)は関数\(f(x)=x^2-2\)を定義します。

secant_method関数は、与えられた初期値defaultを\(x_0\)に代入し、
さらにもう1点\(x_1\)は\(x_0\)に近い地点で開始します。

2点が近いことで接線を近似することが可能です。

指定された収束条件 (精度)precisionと最大反復回数max_stepを使用して、割線法を実行します。

whileループ内で、反復ごとに\(x\)、\( f(x)\)を表示します。
反復0であれば、\(x_0\)、\(x_1\)、\( f(x_0)\)、\( f(x_1)\)を出力して、
次のステップに更新します。

\(|f(x_1)|\gt\)precision が収束条件であり、
指定された精度に達するまで反復が続きます。

もし最大反復回数に達した場合はRuntimeErrorが発生し、
収束しなかった旨が表示されます。

反復の回数が収束条件を満たすと、方程式の解が得られます。

実行例

Google Colaboratoryで実行しています。

初めを0として、収束条件であり、指定された精度に達するまで反復します。
最終的に、収束条件を満たす方程式\(f(x) =0\)の解を出力します。

セカント法で方程式の数値解を出力

方程式の解\(\alpha\)は、\(\alpha\simeq1.414213\)でこの数値は、\(\sqrt{2}\)の数値解です。

グラフの描画

方程式の解の妥当性をグラフで確認します。

解は、\(f(x) =0\)となる\(x \)軸上の\(x \)の値になります。

先ほどのソースコードに続けてグラフと解をプロットします。

# グラフのプロット
plt.figure(figsize=(10, 6))
plt.axhline(0, color='black',linewidth=0.5)
plt.axvline(0, color='black',linewidth=0.5)
x_vals = np.linspace(min(x_values_secant) - 1, max(x_values_secant), 100)
plt.plot(x_vals, f(x_vals), label='f(x)')
plt.scatter(x_values_secant, f_values_secant, color='red', label='Secant Method Iterations')
plt.scatter(result_secant, f(result_secant), color='purple', marker='x', label='Secant Method Solution')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Secant Method')
plt.legend()
plt.show()
割線法で得た数値解の妥当性をグラフで表現

\(f(x) =0\)の\(x\)軸上に方程式の解がプロットされています。

収束の速さの比較

方程式\(f(x)=x^2-2=0 \)を用いて、ニュートン法と割線法の収束性を比較します。

割線法(セカント法)とニュートン法の収束性の速さを比較

割線法は反復回数4に対して、ニュートン法は反復回数3です。
このことから、割線法の方が収束が遅いことを示しています。

2分法は、割線法よりも収束が遅いですが収束性は高い反復法です。

黄金比

割線法について、数列\({x_n}\)が解\(x=\alpha \)に\(p\)次収束することを考えます。
\(p\)の2次方程式\(p^2=p+1\)が得られて、正の解は、

$$p=\frac{1+\sqrt{5}}{2}\simeq1.618$$

となります。一般に割線法は方程式の解によらず、1.618次収束をします。この値を用いた\(1:1.618\)は黄金比と呼ばれ、デザインを美しくする比率として使用されます。

例えば、名刺やクレジットカードなどのカード類、ピラミッド、パルテノン宮殿のような建造物やモナ・リザのような肖像画に使用されています。

ライブラリで割線法を表現

scipy.optimizeモジュールを使うと、割線法を用いた方程式の解の近似ができます。

from scipy.optimize import fsolve

def f(x):
    return x**2 - 2

# 初期値
default = 1.0

# 割線法(セカント法)の実行・表示
result_secant = fsolve(f, default)
print(f"方程式の解: {result_secant}")

まとめ

割線法(セカント法)は、非線形方程式の数値解を求めるための反復的な数値計算手法の一つです。

ニュートン法と同様に、初期推定値から方程式の解に収束するように逐次的な近似を行います。
一方でニュートン法とは異なり、導関数(接線の傾き)を用いる代わりに、近似的な接線を前回の推定点と現在の推定点を通る直線として用います。

導関数を計算する必要がないので、導関数を計算できない場合にも適用できます。ただし、収束の速さはニュートン法よりも遅いので、計算量は増えます。

Udemyで学習する

この記事を書いた人

プロフィール

アリッシア

                 

大学4年間で何か胸を張れるスキルを身に着けたくて当サイト運営を始めました。
現在、大学院に進学するか就職するか迷いながら勉強しています。
詳しいプロフィールはこちら

Contact icon

contact

X icon

X

Instagram icon

Instagram

Note icon

Note

スポンサーリンク
Python
フォローする
タイトルとURLをコピーしました