-
Notifications
You must be signed in to change notification settings - Fork 0
/
rootfinder.go
176 lines (151 loc) · 3.78 KB
/
rootfinder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package rootfinder
import (
"errors"
"math"
)
// Function defines a custom type for the function and derivative of it
type Function func(float64) float64
// RootFinder defines a custom object for root finding
type RootFinder interface {
Bisection(a, b float64) (root float64, iter int, err error)
NewtonRaphson(initialGuess float64) (root float64, iter int, err error)
Secant(initialGuesses ...float64) (root float64, iter int, err error)
}
type rootFinder struct {
maxIteration int
epsilon float64
function Function
derivative Function
}
// New returns a RootFinder object
func New(precision, maxIterarion int, function Function, derivative ...Function) RootFinder {
if function == nil {
panic("function is required")
}
if precision <= 0 {
precision = 2
}
if maxIterarion <= 0 {
maxIterarion = 100
}
rf := rootFinder{
epsilon: math.Pow10(-precision),
maxIteration: maxIterarion,
function: function,
}
if len(derivative) > 0 {
rf.derivative = derivative[0]
}
return rf
}
// Bisection finds root of the function by using bisection method
func (rf rootFinder) Bisection(a, b float64) (root float64, iter int, err error) {
root = 0
iter = 0
err = nil
next := func(a, b, x0 float64, f Function) (float64, float64, float64) {
iter++
if f(a)*f(x0) < 0 {
return a, x0, (a + x0) / 2
}
return x0, b, (x0 + b) / 2
}
stop := func(x0, x1 float64, iter int) bool {
return math.Abs(rf.function(x1)) <= rf.epsilon || math.Abs(x1-x0) <= rf.epsilon
}
if math.Abs(rf.function(a)) <= rf.epsilon {
root = a
return
}
if math.Abs(rf.function(b)) <= rf.epsilon {
root = b
return
}
if rf.function(a)*rf.function(b) > 0 {
err = errors.New("[a,b] is invalid interval")
return
}
x0 := (a + b) / 2
a, b, x1 := next(a, b, x0, rf.function)
for iter <= rf.maxIteration && !stop(x0, x1, iter) {
x0 = x1
a, b, x1 = next(a, b, x0, rf.function)
}
if !stop(x0, x1, iter) {
err = errors.New("root not found")
return
}
rounder := 1 / rf.epsilon
root = math.Round(x1*rounder) / rounder
return
}
func derivative(e float64, f Function) Function {
return func(x float64) float64 {
return (f(x+e) - f(x)) / e
}
}
// NewtonRaphson finds root of the function by using newton-raphson method
func (rf rootFinder) NewtonRaphson(initialGuess float64) (root float64, iter int, err error) {
root = 0
iter = 0
err = nil
next := func(x0 float64, f, df Function) float64 {
iter++
return x0 - f(x0)/df(x0)
}
stop := func(x0, x1 float64) bool {
return math.Abs(rf.function(x1)) <= rf.epsilon || math.Abs(x1-x0) <= rf.epsilon
}
if rf.derivative == nil {
rf.derivative = derivative(rf.epsilon, rf.function)
}
x0 := initialGuess
x1 := next(x0, rf.function, rf.derivative)
for iter <= rf.maxIteration && !stop(x0, x1) {
x0 = x1
x1 = next(x0, rf.function, rf.derivative)
}
if !stop(x0, x1) {
err = errors.New("root not found")
return
}
rounder := 1 / rf.epsilon
root = math.Round(x1*rounder) / rounder
return
}
// Secant finds root of the function by using secant method
func (rf rootFinder) Secant(initialGuesses ...float64) (root float64, iter int, err error) {
root = 0
iter = 0
err = nil
next := func(x0, x1 float64, f Function) float64 {
iter++
return (x0*f(x1) - x1*f(x0)) / (f(x1) - f(x0))
}
stop := func(x0, x1 float64) bool {
return math.Abs(x1-x0) <= rf.epsilon || math.Abs(x1-x0) <= rf.epsilon
}
x0 := 0.0
x1 := 1.0
if len(initialGuesses) > 2 {
x1 = initialGuesses[1]
}
if len(initialGuesses) > 1 {
x0 = initialGuesses[0]
}
x2 := next(x0, x1, rf.function)
x0 = x1
x1 = x2
for iter <= rf.maxIteration && !stop(x0, x1) {
x2 = next(x0, x1, rf.function)
x0 = x1
x1 = x2
}
if !stop(x0, x1) {
err = errors.New("root not found")
return
}
rounder := 1 / rf.epsilon
root = math.Round(x1*rounder) / rounder
return
}