1 | // Copyright (c) 2014 The mathutil Authors. All rights reserved.
|
---|
2 | // Use of this source code is governed by a BSD-style
|
---|
3 | // license that can be found in the LICENSE file.
|
---|
4 |
|
---|
5 | package mathutil // import "modernc.org/mathutil"
|
---|
6 |
|
---|
7 | import (
|
---|
8 | "fmt"
|
---|
9 | "math"
|
---|
10 | "math/big"
|
---|
11 | )
|
---|
12 |
|
---|
13 | // FC32 is a full cycle PRNG covering the 32 bit signed integer range.
|
---|
14 | // In contrast to full cycle generators shown at e.g. http://en.wikipedia.org/wiki/Full_cycle,
|
---|
15 | // this code doesn't produce values at constant delta (mod cycle length).
|
---|
16 | // The 32 bit limit is per this implementation, the algorithm used has no intrinsic limit on the cycle size.
|
---|
17 | // Properties include:
|
---|
18 | // - Adjustable limits on creation (hi, lo).
|
---|
19 | // - Positionable/randomly accessible (Pos, Seek).
|
---|
20 | // - Repeatable (deterministic).
|
---|
21 | // - Can run forward or backward (Next, Prev).
|
---|
22 | // - For a billion numbers cycle the Next/Prev PRN can be produced in cca 100-150ns.
|
---|
23 | // That's like 5-10 times slower compared to PRNs generated using the (non FC) rand package.
|
---|
24 | type FC32 struct {
|
---|
25 | cycle int64 // On average: 3 * delta / 2, (HQ: 2 * delta)
|
---|
26 | delta int64 // hi - lo
|
---|
27 | factors [][]int64 // This trades some space for hopefully a bit of speed (multiple adding vs multiplying).
|
---|
28 | lo int
|
---|
29 | mods []int // pos % set
|
---|
30 | pos int64 // Within cycle.
|
---|
31 | primes []int64 // Ordered. ∏ primes == cycle.
|
---|
32 | set []int64 // Reordered primes (magnitude order bases) according to seed.
|
---|
33 | }
|
---|
34 |
|
---|
35 | // NewFC32 returns a newly created FC32 adjusted for the closed interval [lo, hi] or an Error if any.
|
---|
36 | // If hq == true then trade some generation time for improved (pseudo)randomness.
|
---|
37 | func NewFC32(lo, hi int, hq bool) (r *FC32, err error) {
|
---|
38 | if lo > hi {
|
---|
39 | return nil, fmt.Errorf("invalid range %d > %d", lo, hi)
|
---|
40 | }
|
---|
41 |
|
---|
42 | if uint64(hi)-uint64(lo) > math.MaxUint32 {
|
---|
43 | return nil, fmt.Errorf("range out of int32 limits %d, %d", lo, hi)
|
---|
44 | }
|
---|
45 |
|
---|
46 | delta := int64(hi) - int64(lo)
|
---|
47 | // Find the primorial covering whole delta
|
---|
48 | n, set, p := int64(1), []int64{}, uint32(2)
|
---|
49 | if hq {
|
---|
50 | p++
|
---|
51 | }
|
---|
52 | for {
|
---|
53 | set = append(set, int64(p))
|
---|
54 | n *= int64(p)
|
---|
55 | if n > delta {
|
---|
56 | break
|
---|
57 | }
|
---|
58 | p, _ = NextPrime(p)
|
---|
59 | }
|
---|
60 |
|
---|
61 | // Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta])
|
---|
62 | // while keeping the cardinality of the set (correlates with the statistic "randomness quality")
|
---|
63 | // at max, i.e. discard atmost one member.
|
---|
64 | i := -1 // no candidate prime
|
---|
65 | if n > 2*(delta+1) {
|
---|
66 | for j, p := range set {
|
---|
67 | q := n / p
|
---|
68 | if q < delta+1 {
|
---|
69 | break
|
---|
70 | }
|
---|
71 |
|
---|
72 | i = j // mark the highest candidate prime set index
|
---|
73 | }
|
---|
74 | }
|
---|
75 | if i >= 0 { // shrink the inner cycle
|
---|
76 | n = n / set[i]
|
---|
77 | set = delete(set, i)
|
---|
78 | }
|
---|
79 | r = &FC32{
|
---|
80 | cycle: n,
|
---|
81 | delta: delta,
|
---|
82 | factors: make([][]int64, len(set)),
|
---|
83 | lo: lo,
|
---|
84 | mods: make([]int, len(set)),
|
---|
85 | primes: set,
|
---|
86 | }
|
---|
87 | r.Seed(1) // the default seed should be always non zero
|
---|
88 | return
|
---|
89 | }
|
---|
90 |
|
---|
91 | // Cycle reports the length of the inner FCPRNG cycle.
|
---|
92 | // Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1).
|
---|
93 | func (r *FC32) Cycle() int64 {
|
---|
94 | return r.cycle
|
---|
95 | }
|
---|
96 |
|
---|
97 | // Next returns the first PRN after Pos.
|
---|
98 | func (r *FC32) Next() int {
|
---|
99 | return r.step(1)
|
---|
100 | }
|
---|
101 |
|
---|
102 | // Pos reports the current position within the inner cycle.
|
---|
103 | func (r *FC32) Pos() int64 {
|
---|
104 | return r.pos
|
---|
105 | }
|
---|
106 |
|
---|
107 | // Prev return the first PRN before Pos.
|
---|
108 | func (r *FC32) Prev() int {
|
---|
109 | return r.step(-1)
|
---|
110 | }
|
---|
111 |
|
---|
112 | // Seed uses the provided seed value to initialize the generator to a deterministic state.
|
---|
113 | // A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds.
|
---|
114 | // Still, the FC property holds for any seed value.
|
---|
115 | func (r *FC32) Seed(seed int64) {
|
---|
116 | u := uint64(seed)
|
---|
117 | r.set = mix(r.primes, &u)
|
---|
118 | n := int64(1)
|
---|
119 | for i, p := range r.set {
|
---|
120 | k := make([]int64, p)
|
---|
121 | v := int64(0)
|
---|
122 | for j := range k {
|
---|
123 | k[j] = v
|
---|
124 | v += n
|
---|
125 | }
|
---|
126 | n *= p
|
---|
127 | r.factors[i] = mix(k, &u)
|
---|
128 | }
|
---|
129 | }
|
---|
130 |
|
---|
131 | // Seek sets Pos to |pos| % Cycle.
|
---|
132 | func (r *FC32) Seek(pos int64) { //vet:ignore
|
---|
133 | if pos < 0 {
|
---|
134 | pos = -pos
|
---|
135 | }
|
---|
136 | pos %= r.cycle
|
---|
137 | r.pos = pos
|
---|
138 | for i, p := range r.set {
|
---|
139 | r.mods[i] = int(pos % p)
|
---|
140 | }
|
---|
141 | }
|
---|
142 |
|
---|
143 | func (r *FC32) step(dir int) int {
|
---|
144 | for { // avg loops per step: 3/2 (HQ: 2)
|
---|
145 | y := int64(0)
|
---|
146 | pos := r.pos
|
---|
147 | pos += int64(dir)
|
---|
148 | switch {
|
---|
149 | case pos < 0:
|
---|
150 | pos = r.cycle - 1
|
---|
151 | case pos >= r.cycle:
|
---|
152 | pos = 0
|
---|
153 | }
|
---|
154 | r.pos = pos
|
---|
155 | for i, mod := range r.mods {
|
---|
156 | mod += dir
|
---|
157 | p := int(r.set[i])
|
---|
158 | switch {
|
---|
159 | case mod < 0:
|
---|
160 | mod = p - 1
|
---|
161 | case mod >= p:
|
---|
162 | mod = 0
|
---|
163 | }
|
---|
164 | r.mods[i] = mod
|
---|
165 | y += r.factors[i][mod]
|
---|
166 | }
|
---|
167 | if y <= r.delta {
|
---|
168 | return int(y) + r.lo
|
---|
169 | }
|
---|
170 | }
|
---|
171 | }
|
---|
172 |
|
---|
173 | func delete(set []int64, i int) (y []int64) {
|
---|
174 | for j, v := range set {
|
---|
175 | if j != i {
|
---|
176 | y = append(y, v)
|
---|
177 | }
|
---|
178 | }
|
---|
179 | return
|
---|
180 | }
|
---|
181 |
|
---|
182 | func mix(set []int64, seed *uint64) (y []int64) {
|
---|
183 | for len(set) != 0 {
|
---|
184 | *seed = rol(*seed)
|
---|
185 | i := int(*seed % uint64(len(set)))
|
---|
186 | y = append(y, set[i])
|
---|
187 | set = delete(set, i)
|
---|
188 | }
|
---|
189 | return
|
---|
190 | }
|
---|
191 |
|
---|
192 | func rol(u uint64) (y uint64) {
|
---|
193 | y = u << 1
|
---|
194 | if int64(u) < 0 {
|
---|
195 | y |= 1
|
---|
196 | }
|
---|
197 | return
|
---|
198 | }
|
---|
199 |
|
---|
200 | // FCBig is a full cycle PRNG covering ranges outside of the int32 limits.
|
---|
201 | // For more info see the FC32 docs.
|
---|
202 | // Next/Prev PRN on a 1e15 cycle can be produced in about 2 µsec.
|
---|
203 | type FCBig struct {
|
---|
204 | cycle *big.Int // On average: 3 * delta / 2, (HQ: 2 * delta)
|
---|
205 | delta *big.Int // hi - lo
|
---|
206 | factors [][]*big.Int // This trades some space for hopefully a bit of speed (multiple adding vs multiplying).
|
---|
207 | lo *big.Int
|
---|
208 | mods []int // pos % set
|
---|
209 | pos *big.Int // Within cycle.
|
---|
210 | primes []int64 // Ordered. ∏ primes == cycle.
|
---|
211 | set []int64 // Reordered primes (magnitude order bases) according to seed.
|
---|
212 | }
|
---|
213 |
|
---|
214 | // NewFCBig returns a newly created FCBig adjusted for the closed interval [lo, hi] or an Error if any.
|
---|
215 | // If hq == true then trade some generation time for improved (pseudo)randomness.
|
---|
216 | func NewFCBig(lo, hi *big.Int, hq bool) (r *FCBig, err error) {
|
---|
217 | if lo.Cmp(hi) > 0 {
|
---|
218 | return nil, fmt.Errorf("invalid range %d > %d", lo, hi)
|
---|
219 | }
|
---|
220 |
|
---|
221 | delta := big.NewInt(0)
|
---|
222 | delta.Add(delta, hi).Sub(delta, lo)
|
---|
223 |
|
---|
224 | // Find the primorial covering whole delta
|
---|
225 | n, set, pp, p := big.NewInt(1), []int64{}, big.NewInt(0), uint32(2)
|
---|
226 | if hq {
|
---|
227 | p++
|
---|
228 | }
|
---|
229 | for {
|
---|
230 | set = append(set, int64(p))
|
---|
231 | pp.SetInt64(int64(p))
|
---|
232 | n.Mul(n, pp)
|
---|
233 | if n.Cmp(delta) > 0 {
|
---|
234 | break
|
---|
235 | }
|
---|
236 | p, _ = NextPrime(p)
|
---|
237 | }
|
---|
238 |
|
---|
239 | // Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta])
|
---|
240 | // while keeping the cardinality of the set (correlates with the statistic "randomness quality")
|
---|
241 | // at max, i.e. discard atmost one member.
|
---|
242 | dd1 := big.NewInt(1)
|
---|
243 | dd1.Add(dd1, delta)
|
---|
244 | dd2 := big.NewInt(0)
|
---|
245 | dd2.Lsh(dd1, 1)
|
---|
246 | i := -1 // no candidate prime
|
---|
247 | if n.Cmp(dd2) > 0 {
|
---|
248 | q := big.NewInt(0)
|
---|
249 | for j, p := range set {
|
---|
250 | pp.SetInt64(p)
|
---|
251 | q.Set(n)
|
---|
252 | q.Div(q, pp)
|
---|
253 | if q.Cmp(dd1) < 0 {
|
---|
254 | break
|
---|
255 | }
|
---|
256 |
|
---|
257 | i = j // mark the highest candidate prime set index
|
---|
258 | }
|
---|
259 | }
|
---|
260 | if i >= 0 { // shrink the inner cycle
|
---|
261 | pp.SetInt64(set[i])
|
---|
262 | n.Div(n, pp)
|
---|
263 | set = delete(set, i)
|
---|
264 | }
|
---|
265 | r = &FCBig{
|
---|
266 | cycle: n,
|
---|
267 | delta: delta,
|
---|
268 | factors: make([][]*big.Int, len(set)),
|
---|
269 | lo: lo,
|
---|
270 | mods: make([]int, len(set)),
|
---|
271 | pos: big.NewInt(0),
|
---|
272 | primes: set,
|
---|
273 | }
|
---|
274 | r.Seed(1) // the default seed should be always non zero
|
---|
275 | return
|
---|
276 | }
|
---|
277 |
|
---|
278 | // Cycle reports the length of the inner FCPRNG cycle.
|
---|
279 | // Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1).
|
---|
280 | func (r *FCBig) Cycle() *big.Int {
|
---|
281 | return r.cycle
|
---|
282 | }
|
---|
283 |
|
---|
284 | // Next returns the first PRN after Pos.
|
---|
285 | func (r *FCBig) Next() *big.Int {
|
---|
286 | return r.step(1)
|
---|
287 | }
|
---|
288 |
|
---|
289 | // Pos reports the current position within the inner cycle.
|
---|
290 | func (r *FCBig) Pos() *big.Int {
|
---|
291 | return r.pos
|
---|
292 | }
|
---|
293 |
|
---|
294 | // Prev return the first PRN before Pos.
|
---|
295 | func (r *FCBig) Prev() *big.Int {
|
---|
296 | return r.step(-1)
|
---|
297 | }
|
---|
298 |
|
---|
299 | // Seed uses the provided seed value to initialize the generator to a deterministic state.
|
---|
300 | // A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds.
|
---|
301 | // Still, the FC property holds for any seed value.
|
---|
302 | func (r *FCBig) Seed(seed int64) {
|
---|
303 | u := uint64(seed)
|
---|
304 | r.set = mix(r.primes, &u)
|
---|
305 | n := big.NewInt(1)
|
---|
306 | v := big.NewInt(0)
|
---|
307 | pp := big.NewInt(0)
|
---|
308 | for i, p := range r.set {
|
---|
309 | k := make([]*big.Int, p)
|
---|
310 | v.SetInt64(0)
|
---|
311 | for j := range k {
|
---|
312 | k[j] = big.NewInt(0)
|
---|
313 | k[j].Set(v)
|
---|
314 | v.Add(v, n)
|
---|
315 | }
|
---|
316 | pp.SetInt64(p)
|
---|
317 | n.Mul(n, pp)
|
---|
318 | r.factors[i] = mixBig(k, &u)
|
---|
319 | }
|
---|
320 | }
|
---|
321 |
|
---|
322 | // Seek sets Pos to |pos| % Cycle.
|
---|
323 | func (r *FCBig) Seek(pos *big.Int) {
|
---|
324 | r.pos.Set(pos)
|
---|
325 | r.pos.Abs(r.pos)
|
---|
326 | r.pos.Mod(r.pos, r.cycle)
|
---|
327 | mod := big.NewInt(0)
|
---|
328 | pp := big.NewInt(0)
|
---|
329 | for i, p := range r.set {
|
---|
330 | pp.SetInt64(p)
|
---|
331 | r.mods[i] = int(mod.Mod(r.pos, pp).Int64())
|
---|
332 | }
|
---|
333 | }
|
---|
334 |
|
---|
335 | func (r *FCBig) step(dir int) (y *big.Int) {
|
---|
336 | y = big.NewInt(0)
|
---|
337 | d := big.NewInt(int64(dir))
|
---|
338 | for { // avg loops per step: 3/2 (HQ: 2)
|
---|
339 | r.pos.Add(r.pos, d)
|
---|
340 | switch {
|
---|
341 | case r.pos.Sign() < 0:
|
---|
342 | r.pos.Add(r.pos, r.cycle)
|
---|
343 | case r.pos.Cmp(r.cycle) >= 0:
|
---|
344 | r.pos.SetInt64(0)
|
---|
345 | }
|
---|
346 | for i, mod := range r.mods {
|
---|
347 | mod += dir
|
---|
348 | p := int(r.set[i])
|
---|
349 | switch {
|
---|
350 | case mod < 0:
|
---|
351 | mod = p - 1
|
---|
352 | case mod >= p:
|
---|
353 | mod = 0
|
---|
354 | }
|
---|
355 | r.mods[i] = mod
|
---|
356 | y.Add(y, r.factors[i][mod])
|
---|
357 | }
|
---|
358 | if y.Cmp(r.delta) <= 0 {
|
---|
359 | y.Add(y, r.lo)
|
---|
360 | return
|
---|
361 | }
|
---|
362 | y.SetInt64(0)
|
---|
363 | }
|
---|
364 | }
|
---|
365 |
|
---|
366 | func deleteBig(set []*big.Int, i int) (y []*big.Int) {
|
---|
367 | for j, v := range set {
|
---|
368 | if j != i {
|
---|
369 | y = append(y, v)
|
---|
370 | }
|
---|
371 | }
|
---|
372 | return
|
---|
373 | }
|
---|
374 |
|
---|
375 | func mixBig(set []*big.Int, seed *uint64) (y []*big.Int) {
|
---|
376 | for len(set) != 0 {
|
---|
377 | *seed = rol(*seed)
|
---|
378 | i := int(*seed % uint64(len(set)))
|
---|
379 | y = append(y, set[i])
|
---|
380 | set = deleteBig(set, i)
|
---|
381 | }
|
---|
382 | return
|
---|
383 | }
|
---|