source: code/trunk/vendor/modernc.org/mathutil/rnd.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 9.1 KB
Line 
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
5package mathutil // import "modernc.org/mathutil"
6
7import (
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.
24type 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.
37func 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).
93func (r *FC32) Cycle() int64 {
94 return r.cycle
95}
96
97// Next returns the first PRN after Pos.
98func (r *FC32) Next() int {
99 return r.step(1)
100}
101
102// Pos reports the current position within the inner cycle.
103func (r *FC32) Pos() int64 {
104 return r.pos
105}
106
107// Prev return the first PRN before Pos.
108func (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.
115func (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.
132func (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
143func (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
173func 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
182func 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
192func 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.
203type 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.
216func 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).
280func (r *FCBig) Cycle() *big.Int {
281 return r.cycle
282}
283
284// Next returns the first PRN after Pos.
285func (r *FCBig) Next() *big.Int {
286 return r.step(1)
287}
288
289// Pos reports the current position within the inner cycle.
290func (r *FCBig) Pos() *big.Int {
291 return r.pos
292}
293
294// Prev return the first PRN before Pos.
295func (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.
302func (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.
323func (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
335func (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
366func 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
375func 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}
Note: See TracBrowser for help on using the repository browser.