This repository has been archived by the owner on Mar 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathextrand.go
163 lines (148 loc) · 4.69 KB
/
extrand.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright [2022] [thinkgos] [email protected]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package extrand extend rand
package extrand
import (
cryptoRand "crypto/rand"
"encoding/binary"
"math"
"math/rand"
"time"
)
// Uint64 returns a non-negative pseudo-random 64-bit integer as an uint64.
func Uint64() uint64 {
var rd [8]byte
_, err := cryptoRand.Read(rd[:])
if err != nil {
r := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63() + rand.Int63()))
return r.Uint64()
}
return binary.LittleEndian.Uint64(rd[:])
}
// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
func Int63() int64 {
return int64(Uint64() & math.MaxInt64)
}
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
// It panics if n <= 0.
func Int63n(n int64) int64 {
if n <= 0 {
panic("extrand: invalid argument to Int63n")
}
if n&(n-1) == 0 { // n is power of two, can mask
return Int63() & (n - 1)
}
max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
v := Int63()
for v > max {
v = Int63()
}
return v % n
}
// Uint32 returns a pseudo-random 32-bit value as a uint32.
func Uint32() uint32 {
var rd [4]byte
_, err := cryptoRand.Read(rd[:])
if err != nil {
r := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63() + rand.Int63()))
return r.Uint32()
}
return binary.LittleEndian.Uint32(rd[:])
}
// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
func Int31() int32 { return int32(Uint32() & math.MaxInt32) }
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
// It panics if n <= 0.
func Int31n(n int32) int32 {
if n <= 0 {
panic("extrand: invalid argument to Int31n")
}
if n&(n-1) == 0 { // n is power of two, can mask
return Int31() & (n - 1)
}
max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
v := Int31()
for v > max {
v = Int31()
}
return v % n
}
// Int returns a non-negative pseudo-random int.
func Int() int {
u := uint(Int63())
return int(u << 1 >> 1) // clear sign bit if int == int32
}
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
// It panics if n <= 0.
func Intn(n int) int {
if n <= 0 {
panic("extrand: invalid argument to Intn")
}
if n <= math.MaxInt32 {
return int(Int31n(int32(n)))
}
return int(Int63n(int64(n)))
}
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
func Float64() float64 {
// A clearer, simpler implementation would be:
// return float64(r.Int63n(1<<53)) / (1<<53)
// However, Go 1 shipped with
// return float64(r.Int63()) / (1 << 63)
// and we want to preserve that value stream.
//
// There is one bug in the value stream: r.Int63() may be so close
// to 1<<63 that the division rounds up to 1.0, and we've guaranteed
// that the result is always less than 1.0.
//
// We tried to fix this by mapping 1.0 back to 0.0, but since float64
// values near 0 are much denser than near 1, mapping 1 to 0 caused
// a theoretically significant overshoot in the probability of returning 0.
// Instead of that, if we round up to 1, just try again.
// Getting 1 only happens 1/2⁵³ of the time, so most clients
// will not observe it anyway.
again:
f := float64(Int63()) / (1 << 63)
if f == 1 {
goto again // resample; this branch is taken O(never)
}
return f
}
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
func Float32() float32 {
// Same rationale as in Float64: we want to preserve the Go 1 value
// stream except we want to fix it not to return 1.0
// This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64).
again:
f := float32(Float64())
if f == 1 {
goto again // resample; this branch is taken O(very rarely)
}
return f
}
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
func Perm(n int) []int {
m := make([]int, n)
// In the following loop, the iteration when i=0 always swaps m[0] with m[0].
// A change to remove this useless iteration is to assign 1 to i in the init
// statement. But Perm also effects r. Making this change will affect
// the final state of r. So this change can't be made for compatibility
// reasons for Go 1.
for i := 0; i < n; i++ {
j := Intn(i + 1)
m[i] = m[j]
m[j] = i
}
return m
}