gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }