29 June, 2006

[Haskell] Exercise 3.15, 3.16, 3.17

guardとwhereのレイアウトに注意。whereがguardに従属すると判る様にインデントを深くすること。

numberNDroot :: Float -> Float -> Float -> Int
numberNDroot a b c
| d > 0 = 2
| d == 0 = 1
| d < 0 = 0
where
d = b * b - 4.0 * a * c

Main> numberNDroot 1.0 2.0 0.0
2
Main> numberNDroot 1.0 2.0 1.0
1
Main> numberNDroot 1.0 1.0 1.0
0
Main>

これを使って書く。guardでマッチすると抜けるので、高次の係数の非零から調べれば良い。

numberRoots :: Float -> Float -> Float -> Int
numberRoots a b c
| a /= 0.0 = numberNDroots a b c -- non-degenerate eq.
| b /= 0.0 = 1 -- eq. bx+c=0
| c /= 0.0 = 0 -- eq. c=0 -> no x satisfy the eq.
| otherwise = 3 -- a=b=c=0 -> all x satisfy the eq.

Main> numberRoots 1.0 2.0 0.0
2
Main> numberRoots 1.0 2.0 1.0
1
Main> numberRoots 1.0 2.0 0.0
2
Main> numberRoots 0.0 2.0 1.0
1
Main> numberRoots 0.0 0.0 1.0
0
Main> numberRoots 0.0 0.0 0.0
3
Main>

上記を使って解の個数に応じて解を求める。
ちょっと工夫したところは、bの符号に応じて片方だけ求めて、解と係数の関係 αβ = - c/a を使うところ。加減算での桁落ちを防ぐため。

smallerRoot :: Float -> Float -> Float -> Float
smallerRoot a b c
| numberRoots a b c == 0 = 0.0
| numberRoots a b c == 3 = 0.0
| a == 0.0 = -(c/b)
| b >= 0 = (-b - sqrt d) / 2.0 / a
| otherwise = c / a / (largerRoot a b c)
where d = b * b - 4.0 * a * c

largerRoot :: Float -> Float -> Float -> Float
largerRoot a b c
| numberRoots a b c == 0 = 0.0
| numberRoots a b c == 3 = 0.0
| a == 0.0 = -(c/b)
| b <= 0 = (-b + sqrt d) / 2.0 / a
| otherwise = c / a / (smallerRoot a b c)
where d = b * b - 4.0 * a * c

Main> largerRoot 0.0 0.0 0.0
0.0
Main> largerRoot 0.0 0.0 1.0
0.0
Main> largerRoot 0.0 1.0 1.0
-1.0
Main> largerRoot 1.0 1.0 1.0
0.0
Main> largerRoot 1.0 0.0 (-1.0)
1.0
Main> smallerRoot 0.0 0.0 0.0
0.0
Main> smallerRoot 0.0 0.0 1.0
0.0
Main> smallerRoot 0.0 1.0 1.0
-1.0
Main> smallerRoot 1.0 1.0 1.0
0.0
Main> smallerRoot 1.0 0.0 (-1.0)
-1.0
Main> largerRoot 1.0 (-1.0) 0.0000000000000001
1.0
Main> smallerRoot 1.0 (-1.0) 0.0000000000000001
1.0e-16
Main>

1 comment:

Tom Weeks said...

Using Pattern Matching:

smallerRoot 0 0 _ = 0
smallerRoot 0 b c = -c / b
smallerRoot a b c
| numberNDroots a b c > 0 = (-b - sqrt (b^2 - 4*a*c)) / 2*a
| otherwise = 0