rnd.go (9303B)
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 }