-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbigo.go
140 lines (119 loc) · 2.46 KB
/
bigo.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
package bigO
import (
"fmt"
"math"
)
type complexity int
const (
_ = iota
O1 = iota
ON = iota
ON2 = iota
ON3 = iota
OLogN = iota
ONLogN = iota
OLambda = iota
)
var fitCurves = []complexity{O1, ON, ON2, ON3, OLogN, ONLogN}
type BigO struct {
coef float64
rms float64
cplx complexity
}
func (b *BigO) String() string {
return complexity2Str(b.cplx)
}
func complexity2Str(cplx complexity) string {
switch cplx {
case ON:
return "O(N)"
case ON2:
return "O(N^2)"
case ON3:
return "O(N^3)"
case OLogN:
return "O(lg(N))"
case ONLogN:
return "O(Nlg(N))"
case O1:
return "O(1)"
default:
return "f(n)"
}
}
func fittingCurve(cplx complexity) func(float64) float64 {
switch cplx {
case ON:
return func(n float64) float64 {
return n
}
case ON2:
return func(n float64) float64 {
return n * n
}
case ON3:
return func(n float64) float64 {
return n * n * n
}
case OLogN:
return func(n float64) float64 {
return math.Log2(n)
}
case ONLogN:
return func(n float64) float64 {
return n * math.Log2(n)
}
default:
return func(n float64) float64 {
return 1.0
}
}
}
func minimalLeastSq(n []int, times []float64, fittingCurve func(float64) float64) BigO {
sigmaGn := 0.0
sigmaGnSquared := 0.0
sigmaTime := 0.0
sigmaTimeGn := 0.0
floatN := float64(len(n))
// Calculate least square fitting parameter
for i := 0; i < len(n); i++ {
gnI := fittingCurve(float64(n[i]))
sigmaGn += gnI
sigmaGnSquared += gnI * gnI
sigmaTime += times[i]
sigmaTimeGn += times[i] * gnI
}
var result BigO
result.cplx = OLambda
// Calculate complexity.
result.coef = sigmaTimeGn / sigmaGnSquared
// Calculate RMS
rms := 0.0
for i := 0; i < len(n); i++ {
fit := result.coef * fittingCurve(float64(n[i]))
rms += math.Pow(times[i]-fit, 2)
}
// Normalized RMS by the mean of the observed values
mean := sigmaTime / floatN
result.rms = math.Sqrt(rms/floatN) / mean
return result
}
func Estimate(n []int, times []float64) (BigO, error) {
var bestFit BigO
if len(n) != len(times) {
return bestFit, fmt.Errorf("length mismatch between n and times slices")
}
if len(n) < 2 {
return bestFit, fmt.Errorf("need at least 2 runs")
}
bestFit = minimalLeastSq(n, times, fittingCurve(O1))
bestFit.cplx = O1
for _, fit := range fitCurves {
currentFit := minimalLeastSq(n, times, fittingCurve(fit))
if currentFit.rms < bestFit.rms {
bestFit = currentFit
bestFit.cplx = fit
}
}
return bestFit, nil
}