forked from brianvoe/gofakeit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
weighted.go
107 lines (90 loc) · 2.81 KB
/
weighted.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
package gofakeit
import (
"errors"
"math/rand"
)
// Weighted will take in an array of options and weights and return a random selection based upon its indexed weight
func Weighted(options []interface{}, weights []float32) (interface{}, error) {
return weighted(globalFaker.Rand, options, weights)
}
// Weighted will take in an array of options and weights and return a random selection based upon its indexed weight
func (f *Faker) Weighted(options []interface{}, weights []float32) (interface{}, error) {
return weighted(f.Rand, options, weights)
}
// Weighted will take in an array of options and weights and return a random selection based upon its indexed weight
func weighted(r *rand.Rand, options []interface{}, weights []float32) (interface{}, error) {
ol := len(options)
wl := len(weights)
// If options length is 1 just return it back
if ol == 1 {
return options[0], nil
}
// Make sure they are passing in options
if ol == 0 {
return nil, errors.New("didnt pass options")
}
// Make sure they are passing in weights
if wl == 0 {
return nil, errors.New("didnt pass weights")
}
// Make sure they are passing in the same length
if ol != wl {
return nil, errors.New("options and weights need to be the same length")
}
// Compute the discrete cumulative density from the sum of the weights
cdf := make([]float32, wl)
var sumOfWeights float32 = 0.0
for i, weight := range weights {
if i > 0 {
cdf[i] = cdf[i-1] + weight
sumOfWeights += weight
continue
}
cdf[i] = weight
sumOfWeights += weight
}
// Get rand value from a multple of sumOfWeights
randSumOfWeights := r.Float32() * sumOfWeights
var l int = 0
var h int = wl - 1
for l <= h {
m := l + (h-l)/2
if randSumOfWeights <= cdf[m] {
if m == 0 || (m > 0 && randSumOfWeights > cdf[m-1]) {
return options[m], nil
}
h = m - 1
} else {
l = m + 1
}
}
return nil, errors.New("end of function")
}
func addWeightedLookup() {
AddFuncLookup("weighted", Info{
Display: "Weighted",
Category: "misc",
Description: "Randomly select a given option based upon an equal amount of weights",
Example: "[hello, 2, 6.9],[1, 2, 3] => 6.9",
Output: "any",
Params: []Param{
{Field: "options", Display: "Options", Type: "[]string", Description: "Array of any values"},
{Field: "weights", Display: "Weights", Type: "[]float", Description: "Array of weights"},
},
Generate: func(r *rand.Rand, m *MapParams, info *Info) (interface{}, error) {
options, err := info.GetStringArray(m, "options")
if err != nil {
return nil, err
}
weights, err := info.GetFloat32Array(m, "weights")
if err != nil {
return nil, err
}
optionsInterface := make([]interface{}, len(options))
for i, o := range options {
optionsInterface[i] = o
}
return weighted(r, optionsInterface, weights)
},
})
}