// Copyright (c) 2014 The mathutil Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package mathutil // import "modernc.org/mathutil"import ()// FC32 is a full cycle PRNG covering the 32 bit signed integer range.// In contrast to full cycle generators shown at e.g. http://en.wikipedia.org/wiki/Full_cycle,// this code doesn't produce values at constant delta (mod cycle length).// The 32 bit limit is per this implementation, the algorithm used has no intrinsic limit on the cycle size.// Properties include:// - Adjustable limits on creation (hi, lo).// - Positionable/randomly accessible (Pos, Seek).// - Repeatable (deterministic).// - Can run forward or backward (Next, Prev).// - For a billion numbers cycle the Next/Prev PRN can be produced in cca 100-150ns.// That's like 5-10 times slower compared to PRNs generated using the (non FC) rand package.typeFC32struct { cycle int64// On average: 3 * delta / 2, (HQ: 2 * delta) delta int64// hi - lo factors [][]int64// This trades some space for hopefully a bit of speed (multiple adding vs multiplying). lo int mods []int// pos % set pos int64// Within cycle. primes []int64// Ordered. ∏ primes == cycle. set []int64// Reordered primes (magnitude order bases) according to seed.}// NewFC32 returns a newly created FC32 adjusted for the closed interval [lo, hi] or an Error if any.// If hq == true then trade some generation time for improved (pseudo)randomness.func (, int, bool) ( *FC32, error) {if > {returnnil, fmt.Errorf("invalid range %d > %d", , ) }ifuint64()-uint64() > math.MaxUint32 {returnnil, fmt.Errorf("range out of int32 limits %d, %d", , ) } := int64() - int64()// Find the primorial covering whole delta , , := int64(1), []int64{}, uint32(2)if { ++ }for { = append(, int64()) *= int64()if > {break } , _ = NextPrime() }// Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta]) // while keeping the cardinality of the set (correlates with the statistic "randomness quality") // at max, i.e. discard atmost one member. := -1// no candidate primeif > 2*(+1) {for , := range { := / if < +1 {break } = // mark the highest candidate prime set index } }if >= 0 { // shrink the inner cycle = / [] = delete(, ) } = &FC32{cycle: ,delta: ,factors: make([][]int64, len()),lo: ,mods: make([]int, len()),primes: , } .Seed(1) // the default seed should be always non zeroreturn}// Cycle reports the length of the inner FCPRNG cycle.// Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1).func ( *FC32) () int64 {return .cycle}// Next returns the first PRN after Pos.func ( *FC32) () int {return .step(1)}// Pos reports the current position within the inner cycle.func ( *FC32) () int64 {return .pos}// Prev return the first PRN before Pos.func ( *FC32) () int {return .step(-1)}// Seed uses the provided seed value to initialize the generator to a deterministic state.// A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds.// Still, the FC property holds for any seed value.func ( *FC32) ( int64) { := uint64() .set = mix(.primes, &) := int64(1)for , := range .set { := make([]int64, ) := int64(0)for := range { [] = += } *= .factors[] = mix(, &) }}// Seek sets Pos to |pos| % Cycle.func ( *FC32) ( int64) { //vet:ignoreif < 0 { = - } %= .cycle .pos = for , := range .set { .mods[] = int( % ) }}func ( *FC32) ( int) int {for { // avg loops per step: 3/2 (HQ: 2) := int64(0) := .pos += int64()switch {case < 0: = .cycle - 1case >= .cycle: = 0 } .pos = for , := range .mods { += := int(.set[])switch {case < 0: = - 1case >= : = 0 } .mods[] = += .factors[][] }if <= .delta {returnint() + .lo } }}func ( []int64, int) ( []int64) {for , := range {if != { = append(, ) } }return}func ( []int64, *uint64) ( []int64) {forlen() != 0 { * = rol(*) := int(* % uint64(len())) = append(, []) = delete(, ) }return}func ( uint64) ( uint64) { = << 1ifint64() < 0 { |= 1 }return}// FCBig is a full cycle PRNG covering ranges outside of the int32 limits.// For more info see the FC32 docs.// Next/Prev PRN on a 1e15 cycle can be produced in about 2 µsec.typeFCBigstruct { cycle *big.Int// On average: 3 * delta / 2, (HQ: 2 * delta) delta *big.Int// hi - lo factors [][]*big.Int// This trades some space for hopefully a bit of speed (multiple adding vs multiplying). lo *big.Int mods []int// pos % set pos *big.Int// Within cycle. primes []int64// Ordered. ∏ primes == cycle. set []int64// Reordered primes (magnitude order bases) according to seed.}// NewFCBig returns a newly created FCBig adjusted for the closed interval [lo, hi] or an Error if any.// If hq == true then trade some generation time for improved (pseudo)randomness.func (, *big.Int, bool) ( *FCBig, error) {if .Cmp() > 0 {returnnil, fmt.Errorf("invalid range %d > %d", , ) } := big.NewInt(0) .Add(, ).Sub(, )// Find the primorial covering whole delta , , , := big.NewInt(1), []int64{}, big.NewInt(0), uint32(2)if { ++ }for { = append(, int64()) .SetInt64(int64()) .Mul(, )if .Cmp() > 0 {break } , _ = NextPrime() }// Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta]) // while keeping the cardinality of the set (correlates with the statistic "randomness quality") // at max, i.e. discard atmost one member. := big.NewInt(1) .Add(, ) := big.NewInt(0) .Lsh(, 1) := -1// no candidate primeif .Cmp() > 0 { := big.NewInt(0)for , := range { .SetInt64() .Set() .Div(, )if .Cmp() < 0 {break } = // mark the highest candidate prime set index } }if >= 0 { // shrink the inner cycle .SetInt64([]) .Div(, ) = delete(, ) } = &FCBig{cycle: ,delta: ,factors: make([][]*big.Int, len()),lo: ,mods: make([]int, len()),pos: big.NewInt(0),primes: , } .Seed(1) // the default seed should be always non zeroreturn}// Cycle reports the length of the inner FCPRNG cycle.// Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1).func ( *FCBig) () *big.Int {return .cycle}// Next returns the first PRN after Pos.func ( *FCBig) () *big.Int {return .step(1)}// Pos reports the current position within the inner cycle.func ( *FCBig) () *big.Int {return .pos}// Prev return the first PRN before Pos.func ( *FCBig) () *big.Int {return .step(-1)}// Seed uses the provided seed value to initialize the generator to a deterministic state.// A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds.// Still, the FC property holds for any seed value.func ( *FCBig) ( int64) { := uint64() .set = mix(.primes, &) := big.NewInt(1) := big.NewInt(0) := big.NewInt(0)for , := range .set { := make([]*big.Int, ) .SetInt64(0)for := range { [] = big.NewInt(0) [].Set() .Add(, ) } .SetInt64() .Mul(, ) .factors[] = mixBig(, &) }}// Seek sets Pos to |pos| % Cycle.func ( *FCBig) ( *big.Int) { .pos.Set() .pos.Abs(.pos) .pos.Mod(.pos, .cycle) := big.NewInt(0) := big.NewInt(0)for , := range .set { .SetInt64() .mods[] = int(.Mod(.pos, ).Int64()) }}func ( *FCBig) ( int) ( *big.Int) { = big.NewInt(0) := big.NewInt(int64())for { // avg loops per step: 3/2 (HQ: 2) .pos.Add(.pos, )switch {case .pos.Sign() < 0: .pos.Add(.pos, .cycle)case .pos.Cmp(.cycle) >= 0: .pos.SetInt64(0) }for , := range .mods { += := int(.set[])switch {case < 0: = - 1case >= : = 0 } .mods[] = .Add(, .factors[][]) }if .Cmp(.delta) <= 0 { .Add(, .lo)return } .SetInt64(0) }}func ( []*big.Int, int) ( []*big.Int) {for , := range {if != { = append(, ) } }return}func ( []*big.Int, *uint64) ( []*big.Int) {forlen() != 0 { * = rol(*) := int(* % uint64(len())) = append(, []) = deleteBig(, ) }return}
The pages are generated with Goldsv0.3.6. (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.