|
| 1 | +-- https://www.sampou.org/haskell/article/whyfp.html |
| 2 | +easyIntegrate :: (Fractional a) => (a -> a) -> a -> a -> a |
| 3 | +easyIntegrate f a b = (f a + f b) * (b - a) / 2 |
| 4 | + |
| 5 | +integrate :: (Double -> Double) -> Double -> Double -> [Double] |
| 6 | +integrate f a b = |
| 7 | + easyIntegrate f a b |
| 8 | + : zip' |
| 9 | + (integrate f a mid) |
| 10 | + (integrate f mid b) |
| 11 | + where |
| 12 | + mid = (a + b) / 2 |
| 13 | + |
| 14 | +-- fa fm fbの値をキャッシュすることで効率化したバージョン |
| 15 | +-- integrate' :: (Double -> Double) -> Double -> Double -> [Double] |
| 16 | +-- integrate' f a b = integ f a b (f a) (f b) |
| 17 | +-- where |
| 18 | +-- integ f a b fa fb = ((fa + fb) * (b - a) / 2) : zipWith (+) (integ f a m fa fm) (integ f m b fm fb) |
| 19 | +-- m = (a + b) / 2 -- NOTE: この実装だとmとfmが束縛されてしまい、すべてのintegに対して同じ値を使うので不適切 |
| 20 | +-- fm = f m |
| 21 | +integrate' :: (Double -> Double) -> Double -> Double -> [Double] |
| 22 | +integrate' f a b = integ a b (f a) (f b) |
| 23 | + where |
| 24 | + integ a b fa fb = |
| 25 | + let m = (a + b) / 2 |
| 26 | + fm = f m |
| 27 | + in ((fa + fb) * (b - a) / 2) |
| 28 | + : zipWith |
| 29 | + (+) |
| 30 | + (integ a m fa fm) |
| 31 | + (integ m b fm fb) |
| 32 | + |
| 33 | +-- 誤差を消去するリスト変換 |
| 34 | +-- a = a(i) |
| 35 | +-- b = b(i+1) |
| 36 | +-- a(i+1)*(2**n) - a(i) |
| 37 | +-- A = -------------------- |
| 38 | +-- 2**n - 1 |
| 39 | +elimerror :: Int -> [Double] -> [Double] |
| 40 | +elimerror n (a : b : rest) = |
| 41 | + ((b * (2 ** fromIntegral n) - a) / (2 ** fromIntegral n - 1)) : elimerror n (b : rest) |
| 42 | + |
| 43 | +-- nを求められるらしい。 |
| 44 | +order :: [Double] -> Int |
| 45 | +order (a : b : c : _) = |
| 46 | + round (logBase 2 ((a - c) / (b - c) - 1)) |
| 47 | + |
| 48 | +improve :: [Double] -> [Double] |
| 49 | +improve s = elimerror (order s) s |
| 50 | + |
| 51 | +-- 複数回improveすると急速に高い結果をもたらす |
| 52 | +super :: [Double] -> [Double] |
| 53 | +super s = map second (iterate improve s) |
| 54 | + where |
| 55 | + second (a : b : rest) = b |
| 56 | + |
| 57 | +zip' :: (Num a) => [a] -> [a] -> [a] |
| 58 | +zip' (a : s) (b : t) = a + b : (zip' s t) |
| 59 | +zip' _ _ = [] |
| 60 | + |
| 61 | +zip'' :: (Num a) => [a] -> [a] -> [a] |
| 62 | +zip'' as bt = zipWith (+) as bt |
| 63 | + |
| 64 | +-- 許容誤差と近似値よりも差の小さい2つの連続する近似値を探す |
| 65 | +within :: Double -> [Double] -> Double |
| 66 | +within eps (a : b : rest) |
| 67 | + | abs (a - b) <= eps = b -- 絶対誤差 |
| 68 | + | otherwise = within eps (b : rest) |
| 69 | + |
| 70 | +relative :: Double -> [Double] -> Double |
| 71 | +relative eps (a : b : rest) |
| 72 | + | abs (a - b) <= eps * abs b = b -- abs (a - b) / abs b <= epsを変形したもの。いわゆる相対誤差 |
| 73 | + | otherwise = relative eps (b : rest) |
| 74 | + |
| 75 | +square :: Double -> Double |
| 76 | +square x = x * x |
| 77 | + |
| 78 | +main :: IO () |
| 79 | +main = do |
| 80 | + -- print $ zip' [1, 2] [3, 4] -- [4, 6] |
| 81 | + -- print $ zip'' [1, 2] [3, 4] -- [4, 6] |
| 82 | + let eps = 0.001 |
| 83 | + let a = 0 |
| 84 | + let b = 3 |
| 85 | + print $ within eps (integrate' square a b) -- x^2を積分すると(1/3) * x^3なので |
| 86 | + print $ relative eps (integrate' square a b) |
| 87 | + |
| 88 | + -- 高速にsinの積分を求める例 |
| 89 | + -- print $ improve (integrate' sin 0 4) |
| 90 | + |
| 91 | + -- π/4 |
| 92 | + let f x = 1 / (1 + x * x) |
| 93 | + print $ within eps (super (integrate' f 0 1)) |
0 commit comments