Haskellで求根アルゴリズム
こんにちは、やきつか(@sy_t_)です。
最近はめっきり寒くなってきたのに、いつも半袖でいる知り合いがいて怖いです。
友人がHaskellで数値計算の記事を書いていて、全く同じ内容を書くのも面白いかな、といのうがこの記事の動機です。(友人の記事→Haskellで数値計算やってみた - Qiita)
はじめに
タイトルにある通り、今回は求根アルゴリズムの解説とHaskellによる実装を紹介したいと思います。
求根アルゴリズムとは、
二分法
アルゴリズム
二分法は、その名の通り区間を二分していくことで解を求める手法です。
具体的には、
実装
それでは、アルゴリズムに則って実装していきましょう。
ε :: Double ε = 1E-15 --0に限りなく近い正の定数 bisection :: (Double -> Double) --関数 -> Double --区間下限 -> Double --区間上限 -> Double --解 bisection f x1 x2 | abs (f x') < ε = x' -- -ε<f(x)<εならば中点を解として返す | f x1 * f x' > 0 = bisection f x' x2 --同符号ならばx1を中点と交換 | otherwise = bisection f x1 x' --それ以外ならばx2を中点と交換する where x' = (x1+x2)/2 --中点
実装終わり。解説していきます。
は、0とみなす値域を決定する定数で、0に近ければ近いほど解の精度が良くなります。
実際にその処理を実装しているのが次の行です。
abs (f x') < ε = x' -- -ε<f(x)<εならば中点を解として返す
さらにその次の行は、と同符号ならばに置き換え自身に引数として渡し、の場合も同様の処理をしています。(otherwiseとなっているのは、と異符号ならばと同符号なので)
| f x1 * f x' > 0 = bisectionMethod f x' x2 --同符号ならばx1を中点と交換 | otherwise = bisectionMethod f x1 x' --それ以外ならばx2を中点と交換する
ニュートン法
アルゴリズム
ニュートン法は、アイザック・ニュートンに由来する手法で、微分を用いて解を求める手法です。
この手法は次の漸化式によって、端的に表されます。
図で書くとこんな感じ。なぜ、上記のような漸化式になるかというと、
での接線の方程式が
つまり、での接線を引いて、軸と接線が交わった点を次の点とします。
これを繰り返していくことにより、解に近づいていきます。
解の判定は二分法と同様に、εを用いて行います
実装
それでは、実装していきます。
ε :: Double ε = 1E-15 --0に限りなく近い正の定数 newton :: (Double -> Double) --関数 -> (Double -> Double) --導関数 -> Double --初期のx座標 -> Double --解 newton f f' x | abs (f x) < ε = x -- -ε<f(x)<εならば中点を返す | otherwise = newton f f' x' --次の点に進めて渡す where x' = x - f x / f' x --次のx座標
実装終わり。二分法との違いは、引数に導関数が増え、1つのx座標のみを引数にしているところですね。
ただ、この方法では導関数が明確な場合しか使えないので、適当に差分で置き換えて使うことが多いと思います。
ということで、微分を差分で置き換えて実装してみるとこんな感じ
ε :: Double ε = 1E-15 --0に限りなく近い正の定数 newton' :: (Double -> Double) --関数 -> Double --初期のx座標 -> Double --解 newton' f x | abs (f x) < ε = x -- -ε<f(x)<εならば中点を返す | otherwise = newton' f x' --次の点に進めて渡す where dx = 0.00001 --格子幅 f' x = ( f x - f (x-dx) ) / dx --微分ぽいもの x' = x - f x / f' x --次のx座標