-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaliasmethod.go
89 lines (75 loc) · 1.34 KB
/
aliasmethod.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
package aliasmethod
import (
"errors"
"fmt"
"math/rand"
"time"
)
type AliasMethod struct {
rand *rand.Rand
}
func NewAliasMethod() *AliasMethod {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return &AliasMethod{rand: r}
}
func (self *AliasMethod) Random(table *AliasTable) int {
u := self.rand.Float64()
n := self.rand.Intn(table.Len)
if u <= table.Prob[n] {
return int(n)
} else {
return table.Alias[n]
}
}
type AliasTable struct {
Len int
Prob []float64
Alias []int
}
func NewAliasTable(weights []int) (*AliasTable, error) {
n := len(weights)
sum := 0
for _, value := range weights {
sum += value
}
if sum == 0 {
return nil, errors.New("sum of weights is 0.")
}
prob := make([]float64, n)
for i, w := range weights {
prob[i] = float64(w) * float64(n) / float64(sum)
}
h := 0
l := n - 1
hl := make([]int, n)
for i, p := range prob {
if p < 1 {
hl[l] = i
l--
}
if p > 1 {
hl[h] = i
h++
}
}
a := make([]int, n)
for h != 0 && l != n-1 {
j := hl[l+1]
k := hl[h-1]
if 1 < prob[j] {
panic(fmt.Sprintf("MUST: %f <= 1", prob[j]))
}
if prob[k] < 1 {
panic(fmt.Sprintf("MUST: 1 <= %f", prob[k]))
}
a[j] = k
prob[k] -= (1 - prob[j]) // - residual weight
l++
if prob[k] < 1 {
hl[l] = k
l--
h--
}
}
return &AliasTable{Len: n, Prob: prob, Alias: a}, nil
}