github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/lang/fastrand/fastrand.go (about)

     1  // Copyright 2021 ByteDance Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package fastrand is the fastest pseudorandom number generator in Go(multiple-cores).
    16  package fastrand
    17  
    18  import (
    19  	"math/bits"
    20  	"unsafe"
    21  
    22  	"github.com/bytedance/gopkg/internal/runtimex"
    23  )
    24  
    25  // Uint32 returns a pseudo-random 32-bit value as a uint32.
    26  var Uint32 = runtimex.Fastrand
    27  
    28  // Uint64 returns a pseudo-random 64-bit value as a uint64.
    29  func Uint64() uint64 {
    30  	return (uint64(runtimex.Fastrand()) << 32) | uint64(runtimex.Fastrand())
    31  }
    32  
    33  // Int returns a non-negative pseudo-random int.
    34  func Int() int {
    35  	// EQ
    36  	u := uint(Int63())
    37  	return int(u << 1 >> 1) // clear sign bit if int == int32
    38  }
    39  
    40  // Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
    41  func Int31() int32 { return int32(Uint32() & (1<<31 - 1)) }
    42  
    43  // Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
    44  func Int63() int64 {
    45  	// EQ
    46  	return int64(Uint64() & (1<<63 - 1))
    47  }
    48  
    49  // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
    50  // It panics if n <= 0.
    51  func Int63n(n int64) int64 {
    52  	// EQ
    53  	if n <= 0 {
    54  		panic("invalid argument to Int63n")
    55  	}
    56  	if n&(n-1) == 0 { // n is power of two, can mask
    57  		return Int63() & (n - 1)
    58  	}
    59  	max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
    60  	v := Int63()
    61  	for v > max {
    62  		v = Int63()
    63  	}
    64  	return v % n
    65  }
    66  
    67  // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
    68  // It panics if n <= 0.
    69  // For implementation details, see:
    70  // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
    71  func Int31n(n int32) int32 {
    72  	// EQ
    73  	if n <= 0 {
    74  		panic("invalid argument to Int31n")
    75  	}
    76  	v := Uint32()
    77  	prod := uint64(v) * uint64(n)
    78  	low := uint32(prod)
    79  	if low < uint32(n) {
    80  		thresh := uint32(-n) % uint32(n)
    81  		for low < thresh {
    82  			v = Uint32()
    83  			prod = uint64(v) * uint64(n)
    84  			low = uint32(prod)
    85  		}
    86  	}
    87  	return int32(prod >> 32)
    88  }
    89  
    90  // Intn returns, as an int, a non-negative pseudo-random number in [0,n).
    91  // It panics if n <= 0.
    92  func Intn(n int) int {
    93  	// EQ
    94  	if n <= 0 {
    95  		panic("invalid argument to Intn")
    96  	}
    97  	if n <= 1<<31-1 {
    98  		return int(Int31n(int32(n)))
    99  	}
   100  	return int(Int63n(int64(n)))
   101  }
   102  
   103  func Float64() float64 {
   104  	// EQ
   105  	return float64(Int63n(1<<53)) / (1 << 53)
   106  }
   107  
   108  func Float32() float32 {
   109  	// EQ
   110  	return float32(Int31n(1<<24)) / (1 << 24)
   111  }
   112  
   113  // Uint32n returns a pseudo-random number in [0,n).
   114  //go:nosplit
   115  func Uint32n(n uint32) uint32 {
   116  	// This is similar to Uint32() % n, but faster.
   117  	// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
   118  	return uint32(uint64(Uint32()) * uint64(n) >> 32)
   119  }
   120  
   121  // Uint64n returns a pseudo-random number in [0,n).
   122  func Uint64n(n uint64) uint64 {
   123  	return Uint64() % n
   124  }
   125  
   126  // wyrand: https://github.com/wangyi-fudan/wyhash
   127  type wyrand uint64
   128  
   129  func _wymix(a, b uint64) uint64 {
   130  	hi, lo := bits.Mul64(a, b)
   131  	return hi ^ lo
   132  }
   133  
   134  func (r *wyrand) Uint64() uint64 {
   135  	*r += wyrand(0xa0761d6478bd642f)
   136  	return _wymix(uint64(*r), uint64(*r^wyrand(0xe7037ed1a0b428db)))
   137  }
   138  
   139  func (r *wyrand) Uint64n(n uint64) uint64 {
   140  	return r.Uint64() % n
   141  }
   142  
   143  func (r *wyrand) Uint32() uint32 {
   144  	return uint32(Uint64())
   145  }
   146  
   147  func (r *wyrand) Uint32n(n int) uint32 {
   148  	// This is similar to Uint32() % n, but faster.
   149  	// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
   150  	return uint32(uint64(r.Uint32()) * uint64(n) >> 32)
   151  }
   152  
   153  // Read generates len(p) random bytes and writes them into p.
   154  // It always returns len(p) and a nil error.
   155  // It is safe for concurrent use.
   156  func Read(p []byte) (int, error) {
   157  	l := len(p)
   158  	if l == 0 {
   159  		return 0, nil
   160  	}
   161  
   162  	r := wyrand(Uint32())
   163  
   164  	if l >= 8 {
   165  		var i int
   166  		uint64p := *(*[]uint64)(unsafe.Pointer(&p))
   167  		for l >= 8 {
   168  			uint64p[i] = r.Uint64()
   169  			i++
   170  			l -= 8
   171  		}
   172  	}
   173  
   174  	if l > 0 {
   175  		for l > 0 {
   176  			p[len(p)-l] = byte(r.Uint64() >> (l * 8))
   177  			l--
   178  		}
   179  	}
   180  
   181  	return len(p), nil
   182  }
   183  
   184  // Shuffle pseudo-randomizes the order of elements.
   185  // n is the number of elements. Shuffle panics if n < 0.
   186  // swap swaps the elements with indexes i and j.
   187  func Shuffle(n int, swap func(i, j int)) {
   188  	if n < 0 {
   189  		panic("invalid argument to Shuffle")
   190  	}
   191  	// Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
   192  	// Shuffle really ought not be called with n that doesn't fit in 32 bits.
   193  	// Not only will it take a very long time, but with 2³¹! possible permutations,
   194  	// there's no way that any PRNG can have a big enough internal state to
   195  	// generate even a minuscule percentage of the possible permutations.
   196  	// Nevertheless, the right API signature accepts an int n, so handle it as best we can.
   197  	i := n - 1
   198  	for ; i > 1<<31-1-1; i-- {
   199  		j := int(Int63n(int64(i + 1)))
   200  		swap(i, j)
   201  	}
   202  	for ; i > 0; i-- {
   203  		j := int(Int31n(int32(i + 1)))
   204  		swap(i, j)
   205  	}
   206  }
   207  
   208  // Perm returns, as a slice of n ints, a pseudo-random permutation of the integers
   209  // in the half-open interval [0,n).
   210  func Perm(n int) []int {
   211  	m := make([]int, n)
   212  	for i := 1; i < n; i++ {
   213  		j := Intn(i + 1)
   214  		m[i] = m[j]
   215  		m[j] = i
   216  	}
   217  	return m
   218  }