1
0
mirror of https://github.com/golang/go synced 2024-11-11 19:21:37 -07:00

math/rand/v2: add, optimize N, UintN, Uint32N, Uint64N

Now that we can break the value stream, we can take advantage
of better algorithms that have been suggested since the original
code was written.

Also optimizes IntN, Int32N, Int64N, Perm (indirectly).

All the N variants (IntN, Int32N, Int64N, UintN, N, etc) now
return the same values given a Source and parameter n, so that
for example uint(r.IntN(10)) and r.UintN(10) and r.N(uint(10))
are completely interchangeable.

Int64N4e18 gets slower but that is a near worst case for
the algorithm and is extremely unlikely in practice.

32-bit Int32N variants got slower too, by 15-30%, in exchange
for speeding up everything on 64-bit systems and consistency
across the N functions.

Also rename previously missed benchmark
GlobalInt63Parallel to GlobalInt64Parallel.

goos: linux
goarch: amd64
pkg: math/rand/v2
cpu: AMD Ryzen 9 7950X 16-Core Processor
                        │ 11ad9fdddc.amd64 │            4d84a369d1.amd64            │
                        │      sec/op      │    sec/op     vs base                  │
SourceUint64-32                1.335n ± 1%    1.348n ± 2%        ~ (p=0.335 n=20)
GlobalInt64-32                 2.046n ± 1%    2.082n ± 2%        ~ (p=0.310 n=20)
GlobalInt63Parallel-32        0.1037n ± 1%
GlobalInt64Parallel-32                       0.1036n ± 1%
GlobalUint64-32                2.075n ± 0%    2.077n ± 2%        ~ (p=0.228 n=20)
GlobalUint64Parallel-32       0.1013n ± 1%   0.1012n ± 1%        ~ (p=0.878 n=20)
Int64-32                       1.726n ± 2%    1.750n ± 0%   +1.39% (p=0.000 n=20)
Uint64-32                      1.673n ± 1%    1.707n ± 2%   +2.03% (p=0.002 n=20)
GlobalIntN1000-32              3.895n ± 2%    3.192n ± 1%  -18.05% (p=0.000 n=20)
IntN1000-32                    3.403n ± 1%    2.462n ± 2%  -27.65% (p=0.000 n=20)
Int64N1000-32                  3.053n ± 2%    2.470n ± 1%  -19.11% (p=0.000 n=20)
Int64N1e8-32                   2.718n ± 1%    2.503n ± 2%   -7.91% (p=0.000 n=20)
Int64N1e9-32                   2.712n ± 1%    2.487n ± 1%   -8.31% (p=0.000 n=20)
Int64N2e9-32                   2.690n ± 1%    2.487n ± 1%   -7.57% (p=0.000 n=20)
Int64N1e18-32                  3.084n ± 2%    3.006n ± 2%   -2.53% (p=0.000 n=20)
Int64N2e18-32                  4.026n ± 1%    3.368n ± 1%  -16.33% (p=0.000 n=20)
Int64N4e18-32                  4.049n ± 2%    4.763n ± 1%  +17.62% (p=0.000 n=20)
Int32N1000-32                  2.730n ± 0%    2.403n ± 1%  -11.94% (p=0.000 n=20)
Int32N1e8-32                   2.916n ± 2%    2.405n ± 1%  -17.53% (p=0.000 n=20)
Int32N1e9-32                   3.375n ± 1%    2.402n ± 2%  -28.83% (p=0.000 n=20)
Int32N2e9-32                   3.292n ± 1%    2.384n ± 1%  -27.58% (p=0.000 n=20)
Float32-32                     2.673n ± 1%    2.641n ± 2%        ~ (p=0.147 n=20)
Float64-32                     2.485n ± 1%    2.483n ± 1%        ~ (p=0.804 n=20)
ExpFloat64-32                  3.577n ± 2%    3.486n ± 2%   -2.57% (p=0.000 n=20)
NormFloat64-32                 3.797n ± 2%    3.648n ± 1%   -3.92% (p=0.000 n=20)
Perm3-32                       35.79n ± 2%    33.04n ± 1%   -7.68% (p=0.000 n=20)
Perm30-32                      205.1n ± 1%    171.9n ± 1%  -16.14% (p=0.000 n=20)
Perm30ViaShuffle-32            111.2n ± 2%    100.3n ± 1%   -9.76% (p=0.000 n=20)
ShuffleOverhead-32             100.5n ± 2%    102.5n ± 1%   +1.99% (p=0.007 n=20)
Concurrent-32                  2.188n ± 5%    2.101n ± 0%        ~ (p=0.013 n=20)

goos: darwin
goarch: arm64
pkg: math/rand/v2
cpu: Apple M1
                       │ 11ad9fdddc.arm64 │            4d84a369d1.arm64            │
                       │      sec/op      │    sec/op     vs base                  │
SourceUint64-8                2.272n ± 1%    2.261n ± 1%        ~ (p=0.172 n=20)
GlobalInt64-8                 2.155n ± 1%    2.160n ± 1%        ~ (p=0.482 n=20)
GlobalInt63Parallel-8        0.4352n ± 0%
GlobalInt64Parallel-8                       0.4299n ± 0%
GlobalUint64-8                2.173n ± 1%    2.169n ± 1%        ~ (p=0.262 n=20)
GlobalUint64Parallel-8       0.4340n ± 0%   0.4293n ± 1%   -1.08% (p=0.000 n=20)
Int64-8                       2.544n ± 1%    2.473n ± 1%   -2.83% (p=0.000 n=20)
Uint64-8                      2.552n ± 1%    2.453n ± 1%   -3.90% (p=0.000 n=20)
GlobalIntN1000-8              3.856n ± 0%    2.814n ± 2%  -27.02% (p=0.000 n=20)
IntN1000-8                    3.820n ± 0%    2.933n ± 2%  -23.22% (p=0.000 n=20)
Int64N1000-8                  3.219n ± 2%    2.934n ± 2%   -8.85% (p=0.000 n=20)
Int64N1e8-8                   3.221n ± 2%    2.935n ± 2%   -8.91% (p=0.000 n=20)
Int64N1e9-8                   3.276n ± 2%    2.934n ± 2%  -10.44% (p=0.000 n=20)
Int64N2e9-8                   3.217n ± 0%    2.935n ± 2%   -8.78% (p=0.000 n=20)
Int64N1e18-8                  3.502n ± 2%    3.778n ± 1%   +7.91% (p=0.000 n=20)
Int64N2e18-8                  4.968n ± 1%    4.359n ± 1%  -12.26% (p=0.000 n=20)
Int64N4e18-8                  4.963n ± 0%    6.546n ± 1%  +31.92% (p=0.000 n=20)
Int32N1000-8                  3.189n ± 1%    2.940n ± 2%   -7.81% (p=0.000 n=20)
Int32N1e8-8                   3.514n ± 1%    2.937n ± 2%  -16.41% (p=0.000 n=20)
Int32N1e9-8                   4.133n ± 0%    2.938n ± 0%  -28.91% (p=0.000 n=20)
Int32N2e9-8                   4.137n ± 0%    2.938n ± 2%  -28.97% (p=0.000 n=20)
Float32-8                     3.468n ± 1%    3.486n ± 0%   +0.52% (p=0.000 n=20)
Float64-8                     3.478n ± 0%    3.480n ± 0%        ~ (p=0.063 n=20)
ExpFloat64-8                  4.563n ± 0%    4.533n ± 0%   -0.67% (p=0.000 n=20)
NormFloat64-8                 4.768n ± 0%    4.764n ± 0%   -0.07% (p=0.001 n=20)
Perm3-8                       28.94n ± 0%    26.66n ± 0%   -7.88% (p=0.000 n=20)
Perm30-8                      175.9n ± 0%    143.4n ± 0%  -18.50% (p=0.000 n=20)
Perm30ViaShuffle-8            152.6n ± 1%    142.9n ± 0%   -6.29% (p=0.000 n=20)
ShuffleOverhead-8             119.6n ± 1%    120.7n ± 0%   +0.96% (p=0.000 n=20)
Concurrent-8                  2.452n ± 3%    2.360n ± 2%   -3.73% (p=0.007 n=20)

goos: linux
goarch: 386
pkg: math/rand/v2
cpu: AMD Ryzen 9 7950X 16-Core Processor
                        │ 11ad9fdddc.386 │             4d84a369d1.386             │
                        │     sec/op     │    sec/op     vs base                  │
SourceUint64-32              2.091n ± 1%    2.101n ± 2%        ~ (p=0.672 n=20)
GlobalInt64-32               3.514n ± 2%    3.518n ± 2%        ~ (p=0.723 n=20)
GlobalInt63Parallel-32      0.3197n ± 0%
GlobalInt64Parallel-32                     0.3206n ± 0%
GlobalUint64-32              3.542n ± 1%    3.538n ± 1%        ~ (p=0.304 n=20)
GlobalUint64Parallel-32     0.3218n ± 0%   0.3231n ± 0%        ~ (p=0.071 n=20)
Int64-32                     2.552n ± 2%    2.554n ± 2%        ~ (p=0.693 n=20)
Uint64-32                    2.566n ± 1%    2.575n ± 2%        ~ (p=0.606 n=20)
GlobalIntN1000-32            5.965n ± 2%    6.292n ± 1%   +5.46% (p=0.000 n=20)
IntN1000-32                  4.652n ± 1%    4.735n ± 1%   +1.77% (p=0.000 n=20)
Int64N1000-32               14.485n ± 1%    5.489n ± 2%  -62.11% (p=0.000 n=20)
Int64N1e8-32                14.675n ± 1%    5.528n ± 2%  -62.33% (p=0.000 n=20)
Int64N1e9-32                16.805n ± 2%    5.438n ± 2%  -67.64% (p=0.000 n=20)
Int64N2e9-32                14.515n ± 1%    5.474n ± 1%  -62.28% (p=0.000 n=20)
Int64N1e18-32               16.165n ± 1%    9.053n ± 1%  -44.00% (p=0.000 n=20)
Int64N2e18-32               17.945n ± 2%    9.685n ± 2%  -46.03% (p=0.000 n=20)
Int64N4e18-32                18.35n ± 2%    12.18n ± 1%  -33.62% (p=0.000 n=20)
Int32N1000-32                3.608n ± 1%    4.862n ± 1%  +34.77% (p=0.000 n=20)
Int32N1e8-32                 3.767n ± 1%    4.758n ± 2%  +26.31% (p=0.000 n=20)
Int32N1e9-32                 4.130n ± 2%    4.772n ± 1%  +15.54% (p=0.000 n=20)
Int32N2e9-32                 4.206n ± 1%    4.847n ± 0%  +15.24% (p=0.000 n=20)
Float32-32                   22.18n ± 4%    22.18n ± 4%        ~ (p=0.195 n=20)
Float64-32                   20.75n ± 4%    21.21n ± 3%        ~ (p=0.394 n=20)
ExpFloat64-32                12.58n ± 3%    12.39n ± 2%        ~ (p=0.032 n=20)
NormFloat64-32               7.920n ± 3%    7.422n ± 1%   -6.29% (p=0.000 n=20)
Perm3-32                     40.27n ± 1%    38.00n ± 2%   -5.65% (p=0.000 n=20)
Perm30-32                    213.2n ± 2%    212.7n ± 1%        ~ (p=0.995 n=20)
Perm30ViaShuffle-32          164.2n ± 2%    187.5n ± 2%  +14.22% (p=0.000 n=20)
ShuffleOverhead-32           134.7n ± 2%    159.7n ± 1%  +18.52% (p=0.000 n=20)
Concurrent-32                3.301n ± 2%    3.470n ± 0%   +5.10% (p=0.000 n=20)

For #61716.

Change-Id: Id1481b04202883cd0b23e21bb58d1bca4e482bd3
Reviewed-on: https://go-review.googlesource.com/c/go/+/502500
Reviewed-by: Rob Pike <r@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Russ Cox 2023-06-06 09:43:20 -04:00 committed by Gopher Robot
parent c7dddb02d3
commit c266587846
6 changed files with 404 additions and 213 deletions

View File

@ -7,6 +7,7 @@ pkg math/rand/v2, func Int32N(int32) int32 #61716
pkg math/rand/v2, func Int64() int64 #61716
pkg math/rand/v2, func Int64N(int64) int64 #61716
pkg math/rand/v2, func IntN(int) int #61716
pkg math/rand/v2, func N[$0 intType]($0) $0 #61716
pkg math/rand/v2, func New(Source) *Rand #61716
pkg math/rand/v2, func NewSource(int64) Source #61716
pkg math/rand/v2, func NewZipf(*Rand, float64, float64, uint64) *Zipf #61716
@ -14,7 +15,10 @@ pkg math/rand/v2, func NormFloat64() float64 #61716
pkg math/rand/v2, func Perm(int) []int #61716
pkg math/rand/v2, func Shuffle(int, func(int, int)) #61716
pkg math/rand/v2, func Uint32() uint32 #61716
pkg math/rand/v2, func Uint32N(uint32) uint32 #61716
pkg math/rand/v2, func Uint64() uint64 #61716
pkg math/rand/v2, func Uint64N(uint64) uint64 #61716
pkg math/rand/v2, func UintN(uint) uint #61716
pkg math/rand/v2, method (*Rand) ExpFloat64() float64 #61716
pkg math/rand/v2, method (*Rand) Float32() float32 #61716
pkg math/rand/v2, method (*Rand) Float64() float64 #61716
@ -28,7 +32,10 @@ pkg math/rand/v2, method (*Rand) NormFloat64() float64 #61716
pkg math/rand/v2, method (*Rand) Perm(int) []int #61716
pkg math/rand/v2, method (*Rand) Shuffle(int, func(int, int)) #61716
pkg math/rand/v2, method (*Rand) Uint32() uint32 #61716
pkg math/rand/v2, method (*Rand) Uint32N(uint32) uint32 #61716
pkg math/rand/v2, method (*Rand) Uint64() uint64 #61716
pkg math/rand/v2, method (*Rand) Uint64N(uint64) uint64 #61716
pkg math/rand/v2, method (*Rand) UintN(uint) uint #61716
pkg math/rand/v2, method (*Zipf) Uint64() uint64 #61716
pkg math/rand/v2, type Rand struct #61716
pkg math/rand/v2, type Source interface { Uint64 } #61716

View File

@ -10,6 +10,7 @@ import (
"os"
"strings"
"text/tabwriter"
"time"
)
// These tests serve as an example but also make sure we don't change
@ -84,15 +85,15 @@ func Example_rand() {
// Output:
// Float32 0.2635776 0.6358173 0.6718283
// Float64 0.628605430454327 0.4504798828572669 0.9562755949377957
// ExpFloat64 0.3362240648200941 1.4256072328483647 0.24354758816173044
// NormFloat64 0.17233959114940064 1.577014951434847 0.04259129641113857
// Int32 1501292890 1486668269 182840835
// Int64 3546343826724305832 5724354148158589552 5239846799706671610
// Uint32 2760229429 296659907 1922395059
// IntN(10) 1 2 5
// Int32N(10) 4 7 8
// Int64N(10) 7 6 3
// Perm [1 4 2 3 0] [4 2 1 3 0] [1 2 4 0 3]
// ExpFloat64 0.10400903165715357 0.28855743344575835 0.20489656480442942
// NormFloat64 -0.5602299711828513 -0.9211692958208376 -1.4262061075859056
// Int32 1817075958 91420417 1486590581
// Int64 5724354148158589552 5239846799706671610 5927547564735367388
// Uint32 2295813601 961197529 3493134579
// IntN(10) 4 5 1
// Int32N(10) 8 5 4
// Int64N(10) 2 6 3
// Perm [3 4 2 1 0] [4 1 2 0 3] [0 2 1 3 4]
}
func ExamplePerm() {
@ -105,6 +106,14 @@ func ExamplePerm() {
// 0
}
func ExampleN() {
// Print an int64 in the half-open interval [0, 100).
fmt.Println(rand.N(int64(100)))
// Sleep for a random duration between 0 and 100 milliseconds.
time.Sleep(rand.N(100 * time.Millisecond))
}
func ExampleShuffle() {
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {

View File

@ -4,10 +4,6 @@
package rand
func Int32NForTest(r *Rand, n int32) int32 {
return r.int31n(n)
}
func GetNormalDistributionParameters() (float64, [128]uint32, [128]float32, [128]float32) {
return rn, kn, wn, fn
}

View File

@ -18,6 +18,7 @@
package rand
import (
"math/bits"
_ "unsafe" // for go:linkname
)
@ -58,21 +59,16 @@ func New(src Source) *Rand {
func (r *Rand) Int64() int64 { return int64(r.src.Uint64() &^ (1 << 63)) }
// Uint32 returns a pseudo-random 32-bit value as a uint32.
func (r *Rand) Uint32() uint32 { return uint32(r.Int64() >> 31) }
func (r *Rand) Uint32() uint32 { return uint32(r.src.Uint64() >> 32) }
// Uint64 returns a pseudo-random 64-bit value as a uint64.
func (r *Rand) Uint64() uint64 {
return r.src.Uint64()
}
func (r *Rand) Uint64() uint64 { return r.src.Uint64() }
// Int32 returns a non-negative pseudo-random 31-bit integer as an int32.
func (r *Rand) Int32() int32 { return int32(r.Int64() >> 32) }
func (r *Rand) Int32() int32 { return int32(r.src.Uint64() >> 33) }
// Int returns a non-negative pseudo-random int.
func (r *Rand) Int() int {
u := uint(r.Int64())
return int(u << 1 >> 1) // clear sign bit if int == int32
}
func (r *Rand) Int() int { return int(uint(r.src.Uint64()) << 1 >> 1) }
// Int64N returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n).
// It panics if n <= 0.
@ -80,15 +76,105 @@ func (r *Rand) Int64N(n int64) int64 {
if n <= 0 {
panic("invalid argument to Int64N")
}
return int64(r.uint64n(uint64(n)))
}
// Uint64N returns, as a uint64, a non-negative pseudo-random number in the half-open interval [0,n).
// It panics if n == 0.
func (r *Rand) Uint64N(n uint64) uint64 {
if n == 0 {
panic("invalid argument to Uint64N")
}
return r.uint64n(n)
}
// uint64n is the no-bounds-checks version of Uint64N.
func (r *Rand) uint64n(n uint64) uint64 {
if is32bit && uint64(uint32(n)) == n {
return uint64(r.uint32n(uint32(n)))
}
if n&(n-1) == 0 { // n is power of two, can mask
return r.Int64() & (n - 1)
return r.Uint64() & (n - 1)
}
max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
v := r.Int64()
for v > max {
v = r.Int64()
// Suppose we have a uint64 x uniform in the range [0,2⁶⁴)
// and want to reduce it to the range [0,n) preserving exact uniformity.
// We can simulate a scaling arbitrary precision x * (n/2⁶⁴) by
// the high bits of a double-width multiply of x*n, meaning (x*n)/2⁶⁴.
// Since there are 2⁶⁴ possible inputs x and only n possible outputs,
// the output is necessarily biased if n does not divide 2⁶⁴.
// In general (x*n)/2⁶⁴ = k for x*n in [k*2⁶⁴,(k+1)*2⁶⁴).
// There are either floor(2⁶⁴/n) or ceil(2⁶⁴/n) possible products
// in that range, depending on k.
// But suppose we reject the sample and try again when
// x*n is in [k*2⁶⁴, k*2⁶⁴+(2⁶⁴%n)), meaning rejecting fewer than n possible
// outcomes out of the 2⁶⁴.
// Now there are exactly floor(2⁶⁴/n) possible ways to produce
// each output value k, so we've restored uniformity.
// To get valid uint64 math, 2⁶⁴ % n = (2⁶⁴ - n) % n = -n % n,
// so the direct implementation of this algorithm would be:
//
// hi, lo := bits.Mul64(r.Uint64(), n)
// thresh := -n % n
// for lo < thresh {
// hi, lo = bits.Mul64(r.Uint64(), n)
// }
//
// That still leaves an expensive 64-bit division that we would rather avoid.
// We know that thresh < n, and n is usually much less than 2⁶⁴, so we can
// avoid the last four lines unless lo < n.
//
// See also:
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
// https://lemire.me/blog/2016/06/30/fast-random-shuffling
hi, lo := bits.Mul64(r.Uint64(), n)
if lo < n {
thresh := -n % n
for lo < thresh {
hi, lo = bits.Mul64(r.Uint64(), n)
}
}
return v % n
return hi
}
// uint32n is an identical computation to uint64n
// but optimized for 32-bit systems.
func (r *Rand) uint32n(n uint32) uint32 {
if n&(n-1) == 0 { // n is power of two, can mask
return uint32(r.Uint64()) & (n - 1)
}
// On 64-bit systems we still use the uint64 code below because
// the probability of a random uint64 lo being < a uint32 n is near zero,
// meaning the unbiasing loop almost never runs.
// On 32-bit systems, here we need to implement that same logic in 32-bit math,
// both to preserve the exact output sequence observed on 64-bit machines
// and to preserve the optimization that the unbiasing loop almost never runs.
//
// We want to compute
// hi, lo := bits.Mul64(r.Uint64(), n)
// In terms of 32-bit halves, this is:
// x1:x0 := r.Uint64()
// 0:hi, lo1:lo0 := bits.Mul64(x1:x0, 0:n)
// Writing out the multiplication in terms of bits.Mul32 allows
// using direct hardware instructions and avoiding
// the computations involving these zeros.
x := r.Uint64()
lo1a, lo0 := bits.Mul32(uint32(x), n)
hi, lo1b := bits.Mul32(uint32(x>>32), n)
lo1, c := bits.Add32(lo1a, lo1b, 0)
hi += c
if lo1 == 0 && lo0 < uint32(n) {
n64 := uint64(n)
thresh := uint32(-n64 % n64)
for lo1 == 0 && lo0 < thresh {
x := r.Uint64()
lo1a, lo0 = bits.Mul32(uint32(x), n)
hi, lo1b = bits.Mul32(uint32(x>>32), n)
lo1, c = bits.Add32(lo1a, lo1b, 0)
hi += c
}
}
return hi
}
// Int32N returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n).
@ -97,51 +183,36 @@ func (r *Rand) Int32N(n int32) int32 {
if n <= 0 {
panic("invalid argument to Int32N")
}
if n&(n-1) == 0 { // n is power of two, can mask
return r.Int32() & (n - 1)
}
max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
v := r.Int32()
for v > max {
v = r.Int32()
}
return v % n
return int32(r.uint64n(uint64(n)))
}
// int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n).
// n must be > 0, but int31n does not check this; the caller must ensure it.
// int31n exists because Int32N is inefficient, but Go 1 compatibility
// requires that the stream of values produced by math/rand/v2 remain unchanged.
// int31n can thus only be used internally, by newly introduced APIs.
//
// For implementation details, see:
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
// https://lemire.me/blog/2016/06/30/fast-random-shuffling
func (r *Rand) int31n(n int32) int32 {
v := r.Uint32()
prod := uint64(v) * uint64(n)
low := uint32(prod)
if low < uint32(n) {
thresh := uint32(-n) % uint32(n)
for low < thresh {
v = r.Uint32()
prod = uint64(v) * uint64(n)
low = uint32(prod)
}
// Uint32N returns, as a uint32, a non-negative pseudo-random number in the half-open interval [0,n).
// It panics if n == 0.
func (r *Rand) Uint32N(n uint32) uint32 {
if n == 0 {
panic("invalid argument to Uint32N")
}
return int32(prod >> 32)
return uint32(r.uint64n(uint64(n)))
}
const is32bit = ^uint(0)>>32 == 0
// IntN returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n).
// It panics if n <= 0.
func (r *Rand) IntN(n int) int {
if n <= 0 {
panic("invalid argument to IntN")
}
if n <= 1<<31-1 {
return int(r.Int32N(int32(n)))
return int(r.uint64n(uint64(n)))
}
// UintN returns, as a uint, a non-negative pseudo-random number in the half-open interval [0,n).
// It panics if n == 0.
func (r *Rand) UintN(n uint) uint {
if n == 0 {
panic("invalid argument to UintN")
}
return int(r.Int64N(int64(n)))
return uint(r.uint64n(uint64(n)))
}
// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0).
@ -214,13 +285,8 @@ func (r *Rand) Shuffle(n int, swap func(i, j int)) {
// there's no way that any PRNG can have a big enough internal state to
// generate even a minuscule percentage of the possible permutations.
// Nevertheless, the right API signature accepts an int n, so handle it as best we can.
i := n - 1
for ; i > 1<<31-1-1; i-- {
j := int(r.Int64N(int64(i + 1)))
swap(i, j)
}
for ; i > 0; i-- {
j := int(r.int31n(int32(i + 1)))
for i := n - 1; i > 0; i-- {
j := int(r.uint64n(uint64(i + 1)))
swap(i, j)
}
}
@ -255,6 +321,16 @@ func Int64() int64 { return globalRand.Int64() }
// from the default Source.
func Uint32() uint32 { return globalRand.Uint32() }
// Uint64N returns, as a uint64, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func Uint64N(n uint64) uint64 { return globalRand.Uint64N(n) }
// Uint32N returns, as a uint32, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func Uint32N(n uint32) uint32 { return globalRand.Uint32N(n) }
// Uint64 returns a pseudo-random 64-bit value as a uint64
// from the default Source.
func Uint64() uint64 { return globalRand.Uint64() }
@ -266,21 +342,41 @@ func Int32() int32 { return globalRand.Int32() }
// Int returns a non-negative pseudo-random int from the default Source.
func Int() int { return globalRand.Int() }
// Int64N returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n)
// Int64N returns, as an int64, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func Int64N(n int64) int64 { return globalRand.Int64N(n) }
// Int32N returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n)
// Int32N returns, as an int32, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func Int32N(n int32) int32 { return globalRand.Int32N(n) }
// IntN returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n)
// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func IntN(n int) int { return globalRand.IntN(n) }
// UintN returns, as a uint, a pseudo-random number in the half-open interval [0,n)
// from the default Source.
// It panics if n <= 0.
func UintN(n uint) uint { return globalRand.UintN(n) }
// N returns a pseudo-random number in the half-open interval [0,n) from the default Source.
// The type parameter Int can be any integer type.
// It panics if n <= 0.
func N[Int intType](n Int) Int {
if n <= 0 {
panic("invalid argument to N")
}
return Int(globalRand.uint64n(uint64(n)))
}
type intType interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0)
// from the default Source.
func Float64() float64 { return globalRand.Float64() }

View File

@ -418,7 +418,6 @@ func TestUniformFactorial(t *testing.T) {
fn func() int
}{
{name: "Int32N", fn: func() int { return int(r.Int32N(int32(nfact))) }},
{name: "int31n", fn: func() int { return int(Int32NForTest(r, int32(nfact))) }},
{name: "Perm", fn: func() int { return encodePerm(r.Perm(n)) }},
{name: "Shuffle", fn: func() int {
// Generate permutation using Shuffle.
@ -437,8 +436,8 @@ func TestUniformFactorial(t *testing.T) {
// See https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test and
// https://www.johndcook.com/Beautiful_Testing_ch10.pdf.
nsamples := 10 * nfact
if nsamples < 200 {
nsamples = 200
if nsamples < 500 {
nsamples = 500
}
samples := make([]float64, nsamples)
for i := range samples {
@ -497,7 +496,7 @@ func BenchmarkGlobalInt64(b *testing.B) {
Sink = uint64(t)
}
func BenchmarkGlobalInt63Parallel(b *testing.B) {
func BenchmarkGlobalInt64Parallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
var t int64
for pb.Next() {
@ -777,3 +776,12 @@ func BenchmarkConcurrent(b *testing.B) {
}
wg.Wait()
}
func TestN(t *testing.T) {
for i := 0; i < 1000; i++ {
v := N(10)
if v < 0 || v >= 10 {
t.Fatalf("N(10) returned %d", v)
}
}
}

View File

@ -79,6 +79,18 @@ func TestRegress(t *testing.T) {
}
x = int(big)
case reflect.Uint:
big := uint64s[repeat%len(uint64s)]
if uint64(uint(big)) != big {
r.Uint64N(big) // what would happen on 64-bit machine, to keep stream in sync
if *update {
t.Fatalf("must run -update on 64-bit machine")
}
p++
continue
}
x = uint(big)
case reflect.Int32:
x = int32s[repeat%len(int32s)]
@ -213,26 +225,26 @@ func replace(t *testing.T, file string, new []byte) {
}
var regressGolden = []any{
float64(0.5872982159059681), // ExpFloat64()
float64(0.5372820936538049), // ExpFloat64()
float64(1.2310533463860203), // ExpFloat64()
float64(0.6776268958872181), // ExpFloat64()
float64(0.04451836051028885), // ExpFloat64()
float64(0.2228940815087735), // ExpFloat64()
float64(0.09850095778902446), // ExpFloat64()
float64(0.18902358546064923), // ExpFloat64()
float64(0.18227281316102673), // ExpFloat64()
float64(0.31155615099079936), // ExpFloat64()
float64(0.9474409467969883), // ExpFloat64()
float64(1.0451058861587306), // ExpFloat64()
float64(0.21497642445756152), // ExpFloat64()
float64(1.4215752287217205), // ExpFloat64()
float64(0.755823964126038), // ExpFloat64()
float64(0.38996764757787583), // ExpFloat64()
float64(0.13309377582841803), // ExpFloat64()
float64(0.2115638815656507), // ExpFloat64()
float64(0.7176288428497417), // ExpFloat64()
float64(0.6120456642749681), // ExpFloat64()
float64(0.1835616265352068), // ExpFloat64()
float64(0.1747899228736829), // ExpFloat64()
float64(2.369801563222863), // ExpFloat64()
float64(1.8580757676846802), // ExpFloat64()
float64(0.35731123690292155), // ExpFloat64()
float64(0.5998175837039783), // ExpFloat64()
float64(0.466149534807967), // ExpFloat64()
float64(1.333748223451787), // ExpFloat64()
float64(0.05019983258513916), // ExpFloat64()
float64(1.4143832256421573), // ExpFloat64()
float64(0.7274094466687158), // ExpFloat64()
float64(0.9595398235158843), // ExpFloat64()
float64(1.3010086894917756), // ExpFloat64()
float64(0.8678483737499929), // ExpFloat64()
float64(0.7958895614497015), // ExpFloat64()
float64(0.12235329704897674), // ExpFloat64()
float64(1.1625413819613253), // ExpFloat64()
float64(1.2603945934386542), // ExpFloat64()
float64(0.22199446394172706), // ExpFloat64()
float64(2.248962105270165), // ExpFloat64()
float32(0.6046603), // Float32()
float32(0.9405091), // Float32()
@ -297,45 +309,45 @@ var regressGolden = []any{
int64(6263450610539110790), // Int()
int64(2015796113853353331), // Int()
int32(1298498081), // Int32()
int32(2019727887), // Int32()
int32(1427131847), // Int32()
int32(939984059), // Int32()
int32(911902081), // Int32()
int32(1474941318), // Int32()
int32(140954425), // Int32()
int32(336122540), // Int32()
int32(208240456), // Int32()
int32(646203300), // Int32()
int32(1106410694), // Int32()
int32(1747278511), // Int32()
int32(460128162), // Int32()
int32(817455089), // Int32()
int32(683024728), // Int32()
int32(1006933274), // Int32()
int32(607811211), // Int32()
int32(629431445), // Int32()
int32(1458323237), // Int32()
int32(469339106), // Int32()
int32(649249040), // Int32()
int32(1009863943), // Int32()
int32(1787307747), // Int32()
int32(1543733853), // Int32()
int32(455951040), // Int32()
int32(737470659), // Int32()
int32(1144219036), // Int32()
int32(1241803094), // Int32()
int32(104120228), // Int32()
int32(1396843474), // Int32()
int32(553205347), // Int32()
int32(873639255), // Int32()
int32(1303805905), // Int32()
int32(408727544), // Int32()
int32(1415254188), // Int32()
int32(503466637), // Int32()
int32(1377647429), // Int32()
int32(1388457546), // Int32()
int32(729161618), // Int32()
int32(1308411377), // Int32()
int32(0), // Int32N(1)
int32(7), // Int32N(10)
int32(7), // Int32N(32)
int32(459963), // Int32N(1048576)
int32(688668), // Int32N(1048577)
int32(474941318), // Int32N(1000000000)
int32(140954425), // Int32N(1073741824)
int32(336122540), // Int32N(2147483646)
int32(208240456), // Int32N(2147483647)
int32(0), // Int32N(1)
int32(4), // Int32N(10)
int32(15), // Int32N(32)
int32(851874), // Int32N(1048576)
int32(613606), // Int32N(1048577)
int32(683024728), // Int32N(1000000000)
int32(1006933274), // Int32N(1073741824)
int32(607811211), // Int32N(2147483646)
int32(629431445), // Int32N(2147483647)
int32(29), // Int32N(32)
int32(883715), // Int32N(1048576)
int32(222632), // Int32N(1048577)
int32(343411536), // Int32N(1000000000)
int32(957743134), // Int32N(1073741824)
int32(1241803092), // Int32N(2147483646)
int32(104120228), // Int32N(2147483647)
int32(0), // Int32N(1)
int32(2), // Int32N(10)
int32(7), // Int32N(32)
int32(96566), // Int32N(1048576)
int32(199574), // Int32N(1048577)
int32(659029087), // Int32N(1000000000)
int32(606492121), // Int32N(1073741824)
int32(1377647428), // Int32N(2147483646)
int32(1388457546), // Int32N(2147483647)
int32(0), // Int32N(1)
int32(6), // Int32N(10)
@ -361,109 +373,130 @@ var regressGolden = []any{
int64(2015796113853353331), // Int64()
int64(0), // Int64N(1)
int64(1), // Int64N(10)
int64(4), // Int64N(10)
int64(29), // Int64N(32)
int64(883715), // Int64N(1048576)
int64(338103), // Int64N(1048577)
int64(549167320), // Int64N(1000000000)
int64(222632), // Int64N(1048577)
int64(343411536), // Int64N(1000000000)
int64(957743134), // Int64N(1073741824)
int64(1927814468), // Int64N(2147483646)
int64(1375471152), // Int64N(2147483647)
int64(775422040480279449), // Int64N(1000000000000000000)
int64(1241803092), // Int64N(2147483646)
int64(104120228), // Int64N(2147483647)
int64(650455930292643530), // Int64N(1000000000000000000)
int64(140311732333010180), // Int64N(1152921504606846976)
int64(7504504064263669287), // Int64N(9223372036854775806)
int64(1976235410884491574), // Int64N(9223372036854775807)
int64(3752252032131834642), // Int64N(9223372036854775806)
int64(5599803723869633690), // Int64N(9223372036854775807)
int64(0), // Int64N(1)
int64(5), // Int64N(10)
int64(6), // Int64N(10)
int64(25), // Int64N(32)
int64(920424), // Int64N(1048576)
int64(345137), // Int64N(1048577)
int64(539110790), // Int64N(1000000000)
int64(677958), // Int64N(1048577)
int64(339542337), // Int64N(1000000000)
int64(701992307), // Int64N(1073741824)
int64(0), // IntN(1)
int64(7), // IntN(10)
int64(7), // IntN(32)
int64(459963), // IntN(1048576)
int64(688668), // IntN(1048577)
int64(474941318), // IntN(1000000000)
int64(140954425), // IntN(1073741824)
int64(336122540), // IntN(2147483646)
int64(208240456), // IntN(2147483647)
int64(775422040480279449), // IntN(1000000000000000000)
int64(4), // IntN(10)
int64(29), // IntN(32)
int64(883715), // IntN(1048576)
int64(222632), // IntN(1048577)
int64(343411536), // IntN(1000000000)
int64(957743134), // IntN(1073741824)
int64(1241803092), // IntN(2147483646)
int64(104120228), // IntN(2147483647)
int64(650455930292643530), // IntN(1000000000000000000)
int64(140311732333010180), // IntN(1152921504606846976)
int64(7504504064263669287), // IntN(9223372036854775806)
int64(1976235410884491574), // IntN(9223372036854775807)
int64(3752252032131834642), // IntN(9223372036854775806)
int64(5599803723869633690), // IntN(9223372036854775807)
int64(0), // IntN(1)
int64(8), // IntN(10)
int64(26), // IntN(32)
int64(685707), // IntN(1048576)
int64(285245), // IntN(1048577)
int64(458323237), // IntN(1000000000)
int64(469339106), // IntN(1073741824)
int64(6), // IntN(10)
int64(25), // IntN(32)
int64(920424), // IntN(1048576)
int64(677958), // IntN(1048577)
int64(339542337), // IntN(1000000000)
int64(701992307), // IntN(1073741824)
float64(-1.233758177597947), // NormFloat64()
float64(-0.12634751070237293), // NormFloat64()
float64(-0.5209945711531503), // NormFloat64()
float64(2.28571911769958), // NormFloat64()
float64(0.3228052526115799), // NormFloat64()
float64(0.5900672875996937), // NormFloat64()
float64(0.15880774017643562), // NormFloat64()
float64(0.9892020842955818), // NormFloat64()
float64(-0.731283016177479), // NormFloat64()
float64(0.6863807850359727), // NormFloat64()
float64(1.585403962280623), // NormFloat64()
float64(0.8382059044208106), // NormFloat64()
float64(1.2988408475174342), // NormFloat64()
float64(0.5273583930598617), // NormFloat64()
float64(0.7324419258045132), // NormFloat64()
float64(-1.0731798210887524), // NormFloat64()
float64(0.7001209024399848), // NormFloat64()
float64(0.4315307186960532), // NormFloat64()
float64(0.9996261210112625), // NormFloat64()
float64(-1.5239676725278932), // NormFloat64()
float64(0.6694336828657225), // NormFloat64()
float64(0.7506128421991493), // NormFloat64()
float64(-0.5466367925077582), // NormFloat64()
float64(-0.8240444698703802), // NormFloat64()
float64(0.11563765115029284), // NormFloat64()
float64(-1.3442355710948637), // NormFloat64()
float64(-1.0654999977586854), // NormFloat64()
float64(0.15938628997241455), // NormFloat64()
float64(-0.8046314635002316), // NormFloat64()
float64(0.8323920113630076), // NormFloat64()
float64(1.0611019472659846), // NormFloat64()
float64(-0.8814992544664111), // NormFloat64()
float64(0.9236344788106081), // NormFloat64()
float64(-1.2854378982224413), // NormFloat64()
float64(0.4683572952232405), // NormFloat64()
float64(-0.5065217527091702), // NormFloat64()
float64(-0.6460803205194869), // NormFloat64()
float64(0.7913615856789362), // NormFloat64()
float64(-1.6119549224461807), // NormFloat64()
float64(0.16216183438701695), // NormFloat64()
[]int{}, // Perm(0)
[]int{0}, // Perm(1)
[]int{0, 3, 2, 4, 1}, // Perm(5)
[]int{3, 7, 0, 1, 6, 2, 4, 5}, // Perm(8)
[]int{2, 3, 7, 6, 1, 8, 0, 5, 4}, // Perm(9)
[]int{5, 2, 6, 4, 3, 7, 8, 9, 1, 0}, // Perm(10)
[]int{0, 11, 2, 5, 14, 7, 3, 1, 13, 8, 9, 4, 10, 6, 12, 15}, // Perm(16)
[]int{0, 4, 2, 1, 3}, // Perm(5)
[]int{2, 4, 5, 0, 7, 1, 3, 6}, // Perm(8)
[]int{6, 4, 1, 5, 7, 3, 0, 8, 2}, // Perm(9)
[]int{8, 0, 1, 2, 3, 9, 5, 4, 7, 6}, // Perm(10)
[]int{0, 13, 14, 7, 1, 4, 15, 10, 11, 12, 9, 5, 3, 6, 8, 2}, // Perm(16)
[]int{}, // Perm(0)
[]int{0}, // Perm(1)
[]int{4, 1, 0, 3, 2}, // Perm(5)
[]int{6, 0, 1, 3, 2, 7, 4, 5}, // Perm(8)
[]int{8, 3, 6, 7, 2, 5, 4, 0, 1}, // Perm(9)
[]int{2, 5, 4, 9, 7, 0, 8, 3, 6, 1}, // Perm(10)
[]int{12, 6, 8, 15, 3, 5, 9, 11, 7, 10, 1, 13, 14, 2, 0, 4}, // Perm(16)
[]int{3, 2, 4, 0, 1}, // Perm(5)
[]int{7, 1, 6, 4, 2, 3, 5, 0}, // Perm(8)
[]int{1, 7, 2, 6, 3, 5, 8, 4, 0}, // Perm(9)
[]int{1, 5, 7, 0, 3, 6, 4, 9, 2, 8}, // Perm(10)
[]int{6, 13, 2, 11, 14, 7, 10, 12, 4, 5, 3, 0, 15, 9, 1, 8}, // Perm(16)
[]int{}, // Perm(0)
[]int{0}, // Perm(1)
[]int{0, 2, 4, 3, 1}, // Perm(5)
[]int{4, 7, 0, 2, 6, 1, 5, 3}, // Perm(8)
[]int{6, 5, 8, 0, 1, 3, 7, 2, 4}, // Perm(9)
[]int{8, 1, 9, 7, 6, 5, 2, 0, 4, 3}, // Perm(10)
[]int{0, 4, 2, 1, 3}, // Perm(5)
[]int{0, 7, 1, 4, 3, 6, 2, 5}, // Perm(8)
[]int{1, 3, 0, 4, 5, 2, 8, 7, 6}, // Perm(9)
[]int{5, 4, 7, 9, 6, 1, 0, 3, 8, 2}, // Perm(10)
uint32(2596996162), // Uint32()
uint32(4039455774), // Uint32()
uint32(2854263694), // Uint32()
uint32(1879968118), // Uint32()
uint32(1823804162), // Uint32()
uint32(2949882636), // Uint32()
uint32(281908850), // Uint32()
uint32(672245080), // Uint32()
uint32(416480912), // Uint32()
uint32(1292406600), // Uint32()
uint32(2212821389), // Uint32()
uint32(3494557023), // Uint32()
uint32(920256325), // Uint32()
uint32(1634910179), // Uint32()
uint32(1366049456), // Uint32()
uint32(2013866549), // Uint32()
uint32(1215622422), // Uint32()
uint32(1258862891), // Uint32()
uint32(2916646474), // Uint32()
uint32(938678213), // Uint32()
uint32(1298498081), // Uint32()
uint32(2019727887), // Uint32()
uint32(3574615495), // Uint32()
uint32(3087467707), // Uint32()
uint32(911902081), // Uint32()
uint32(1474941318), // Uint32()
uint32(2288438073), // Uint32()
uint32(2483606188), // Uint32()
uint32(208240456), // Uint32()
uint32(2793686948), // Uint32()
uint32(1106410694), // Uint32()
uint32(1747278511), // Uint32()
uint32(2607611810), // Uint32()
uint32(817455089), // Uint32()
uint32(2830508376), // Uint32()
uint32(1006933274), // Uint32()
uint32(2755294859), // Uint32()
uint32(2776915093), // Uint32()
uint32(1458323237), // Uint32()
uint32(2616822754), // Uint32()
uint32(0), // Uint32N(1)
uint32(4), // Uint32N(10)
uint32(29), // Uint32N(32)
uint32(883715), // Uint32N(1048576)
uint32(222632), // Uint32N(1048577)
uint32(343411536), // Uint32N(1000000000)
uint32(957743134), // Uint32N(1073741824)
uint32(1241803092), // Uint32N(2147483646)
uint32(104120228), // Uint32N(2147483647)
uint32(2793686946), // Uint32N(4294967294)
uint32(1106410694), // Uint32N(4294967295)
uint32(0), // Uint32N(1)
uint32(6), // Uint32N(10)
uint32(20), // Uint32N(32)
uint32(240907), // Uint32N(1048576)
uint32(245833), // Uint32N(1048577)
uint32(641517075), // Uint32N(1000000000)
uint32(340335899), // Uint32N(1073741824)
uint32(729161617), // Uint32N(2147483646)
uint32(1308411376), // Uint32N(2147483647)
uint64(5577006791947779410), // Uint64()
uint64(8674665223082153551), // Uint64()
@ -485,4 +518,46 @@ var regressGolden = []any{
uint64(11926759511765359899), // Uint64()
uint64(6263450610539110790), // Uint64()
uint64(11239168150708129139), // Uint64()
uint64(0), // Uint64N(1)
uint64(4), // Uint64N(10)
uint64(29), // Uint64N(32)
uint64(883715), // Uint64N(1048576)
uint64(222632), // Uint64N(1048577)
uint64(343411536), // Uint64N(1000000000)
uint64(957743134), // Uint64N(1073741824)
uint64(1241803092), // Uint64N(2147483646)
uint64(104120228), // Uint64N(2147483647)
uint64(650455930292643530), // Uint64N(1000000000000000000)
uint64(140311732333010180), // Uint64N(1152921504606846976)
uint64(3752252032131834642), // Uint64N(9223372036854775806)
uint64(5599803723869633690), // Uint64N(9223372036854775807)
uint64(3510942875414458835), // Uint64N(18446744073709551614)
uint64(12156940908066221322), // Uint64N(18446744073709551615)
uint64(0), // Uint64N(1)
uint64(6), // Uint64N(10)
uint64(27), // Uint64N(32)
uint64(205190), // Uint64N(1048576)
uint64(638873), // Uint64N(1048577)
uint64(0), // UintN(1)
uint64(4), // UintN(10)
uint64(29), // UintN(32)
uint64(883715), // UintN(1048576)
uint64(222632), // UintN(1048577)
uint64(343411536), // UintN(1000000000)
uint64(957743134), // UintN(1073741824)
uint64(1241803092), // UintN(2147483646)
uint64(104120228), // UintN(2147483647)
uint64(650455930292643530), // UintN(1000000000000000000)
uint64(140311732333010180), // UintN(1152921504606846976)
uint64(3752252032131834642), // UintN(9223372036854775806)
uint64(5599803723869633690), // UintN(9223372036854775807)
uint64(3510942875414458835), // UintN(18446744073709551614)
uint64(12156940908066221322), // UintN(18446744073709551615)
uint64(0), // UintN(1)
uint64(6), // UintN(10)
uint64(27), // UintN(32)
uint64(205190), // UintN(1048576)
uint64(638873), // UintN(1048577)
}