-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsolve_cubic.go
More file actions
64 lines (51 loc) · 1.35 KB
/
solve_cubic.go
File metadata and controls
64 lines (51 loc) · 1.35 KB
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
package zfactor
import (
"errors"
"math"
"math/cmplx"
)
// SolveCubic solves ax^3 + bx^2 + cx + d = 0
// Returns all 3 roots (possibly complex).
func SolveCubic(a, b, c, d float64) ([3]complex128, error) {
if a == 0 {
return [3]complex128{}, errors.New("equation provided is not cubic (a = 0)")
}
// 1. Normalize coefficients
b /= a
c /= a
d /= a
// 2. Depressed cubic: y^3 + py + q = 0
p := c - b*b/3
q := 2*b*b*b/27 - b*c/3 + d
// 3. Discriminant
delta := (q*q)/4 + (p*p*p)/27
// 4. Cube roots of unity
omega := complex(-0.5, math.Sqrt(3)/2)
omega2 := complex(-0.5, -math.Sqrt(3)/2)
var roots [3]complex128
if delta >= 0 {
// One real root and two complex
u := cmplx.Pow(complex(-q/2+math.Sqrt(delta), 0), 1.0/3)
v := cmplx.Pow(complex(-q/2-math.Sqrt(delta), 0), 1.0/3)
y1 := u + v
y2 := u*omega + v*omega2
y3 := u*omega2 + v*omega
shift := complex(b/3, 0)
roots[0] = y1 - shift
roots[1] = y2 - shift
roots[2] = y3 - shift
} else {
// Three real roots
r := math.Sqrt(-p * p * p / 27)
phi := math.Acos(-q / (2 * math.Sqrt(-(p*p*p)/27)))
t := 2 * math.Cbrt(r)
y1 := complex(t*math.Cos(phi/3), 0)
y2 := complex(t*math.Cos((phi+2*math.Pi)/3), 0)
y3 := complex(t*math.Cos((phi+4*math.Pi)/3), 0)
shift := complex(b/3, 0)
roots[0] = y1 - shift
roots[1] = y2 - shift
roots[2] = y3 - shift
}
return roots, nil
}