问题:如何在Go中生成唯一随机数的流?
即,我想保证使用a
和/或标准Go库实用程序在数组math/rand
中没有重复。
func RandomNumberGenerator() *rand.Rand {
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
return r1
}
rng := RandomNumberGenerator()
N := 10000
for i := 0; i < N; i++ {
a[i] = rng.Int()
}
有关如何在Go中生成一系列随机数的问题和解决方案,例如here。
但是现在我想要生成一系列不与之前值重复的随机数。在Go中有标准/推荐的方法吗?
我的猜测是(1)使用排列或(2)跟踪先前生成的数字并重新生成一个值(如果之前已生成的话)。
但如果我只想要一些数字和(2)听起来非常耗时,如果我因为碰撞而最终生成一系列随机数,那么解决方法(1)听起来就像是一种矫枉过正,我猜它是也非常耗费内存。
用例:使用没有重复的10K,100K,1M伪随机数对Go程序进行基准测试。
答案 0 :(得分:1)
你绝对应该采用方法2.假设你在64位机器上运行,从而生成63位整数(64位,但rand.Int
永远不会返回负数号)。即使你产生了40亿个数字,但仍然只有1/40的机会,任何给定的数字都是重复的。因此,您几乎永远不必重新生成,而且几乎永远永远不会再生两次。
尝试,例如:
type UniqueRand struct {
generated map[int]bool
}
func (u *UniqueRand) Int() int {
for {
i := rand.Int()
if !u.generated[i] {
u.generated[i] = true
return i
}
}
}
答案 1 :(得分:0)
1- Fast positive and negative int32
unique pseudo random numbers in 296ms using std lib:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
const n = 1000000
rand.Seed(time.Now().UTC().UnixNano())
duplicate := 0
mp := make(map[int32]struct{}, n)
var r int32
t := time.Now()
for i := 0; i < n; {
r = rand.Int31()
if i&1 == 0 {
r = -r
}
if _, ok := mp[r]; ok {
duplicate++
} else {
mp[r] = zero
i++
}
}
fmt.Println(time.Since(t))
fmt.Println("len: ", len(mp))
fmt.Println("duplicate: ", duplicate)
positive := 0
for k := range mp {
if k > 0 {
positive++
}
}
fmt.Println(`n=`, n, `positive=`, positive)
}
var zero = struct{}{}
输出:
296.0169ms
len: 1000000
duplicate: 118
n= 1000000 positive= 500000
2-只需填写map[int32]struct{}
:
for i := int32(0); i < n; i++ {
m[i] = zero
}
阅读时,Go中没有按顺序排列:
for k := range m {
fmt.Print(k, " ")
}
对于1000000个唯一数字,这只需要183毫秒,没有重复(The Go Playground):
package main
import (
"fmt"
"time"
)
func main() {
const n = 1000000
m := make(map[int32]struct{}, n)
t := time.Now()
for i := int32(0); i < n; i++ {
m[i] = zero
}
fmt.Println(time.Since(t))
fmt.Println("len: ", len(m))
// for k := range m {
// fmt.Print(k, " ")
// }
}
var zero = struct{}{}
3-这是简单但很慢的(对于200000个唯一数字需要22秒),因此您可以生成并将其保存到文件一次:
package main
import "time"
import "fmt"
import "math/rand"
func main() {
dup := 0
t := time.Now()
const n = 200000
rand.Seed(time.Now().UTC().UnixNano())
var a [n]int32
var exist bool
for i := 0; i < n; {
r := rand.Int31()
exist = false
for j := 0; j < i; j++ {
if a[j] == r {
dup++
fmt.Println(dup)
exist = true
break
}
}
if !exist {
a[i] = r
i++
}
}
fmt.Println(time.Since(t))
}
答案 2 :(得分:0)
基于@joshlf回答的临时解决方法
type UniqueRand struct {
generated map[int]bool //keeps track of
rng *rand.Rand //underlying random number generator
scope int //scope of number to be generated
}
//Generating unique rand less than N
//If N is less or equal to 0, the scope will be unlimited
//If N is greater than 0, it will generate (-scope, +scope)
//If no more unique number can be generated, it will return -1 forwards
func NewUniqueRand(N int) *UniqueRand{
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
return &UniqueRand{
generated: map[int]bool{},
rng: r1,
scope: N,
}
}
func (u *UniqueRand) Int() int {
if u.scope > 0 && len(u.generated) >= u.scope {
return -1
}
for {
var i int
if u.scope > 0 {
i = u.rng.Int() % u.scope
}else{
i = u.rng.Int()
}
if !u.generated[i] {
u.generated[i] = true
return i
}
}
}
客户端代码
func TestSetGet2(t *testing.T) {
const N = 10000
for _, mask := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} {
rng := NewUniqueRand(2*N)
a := make([]int, N)
for i := 0; i < N; i++ {
a[i] = (rng.Int() ^ mask) << 1
}
//Benchmark Code
}
}
答案 3 :(得分:0)
我认为有两个理由想要这个。您想测试一个随机数生成器,或者您想要唯一的随机数。
我的第一个问题是为什么?有大量可靠的随机数生成器。不要写自己的,它基本上涉及密码学,这从来都不是一个好主意。也许您正在测试使用随机数生成器生成随机输出的系统?
存在一个问题:无法保证随机数是唯一的。他们是随机的。总是有碰撞的可能性。测试随机输出是唯一的是不正确的。
相反,您希望测试结果的 均匀分布 。为此,我将参考another answer about how to test a random number generator。
从实际角度来看,您不需要保证唯一性,但要使碰撞不太可能,这不是一个问题。这是UUIDs的用途。他们是128位普遍独特的标识符。有很多方法可以为特定场景生成它们。
UUIDv4基本上只是一个122位的随机数,它有一些非常小的碰撞机会。 Let's approximate it
n = how many random numbers you'll generate
M = size of the keyspace (2^122 for a 122 bit random number)
P = probability of collision
P = n^2/2M
解决n ...
n = sqrt(2MP)
将P设置为荒谬的东西,如1e-12(万亿分之一),我们发现你可以产生大约3.2万亿UUIDv4,其中有1万亿次碰撞。赢得彩票的可能性是3.2万亿UUIDv4中碰撞的1000倍。我认为这是可以接受的。
这是a UUIDv4 library in Go to use以及生成100万个唯一随机128位值的演示。
package main
import (
"fmt"
"github.com/frankenbeanies/uuid4"
)
func main() {
for i := 0; i <= 1000000; i++ {
uuid := uuid4.New().Bytes()
// use the uuid
}
}
答案 4 :(得分:0)
我正在手机上打字,所以请原谅缺少代码或格式不正确。
我就是这样做的:
生成有序唯一数字列表。
选择任意两个随机索引并交换它们的元素。
继续交换一定次数的迭代次数。
您剩下的切片是您的随机唯一列表。
注意:
这很简单,内存使用与大小成正比
可以随时生成和随机化列表,甚至可以使用go generate
进行预编译当你想要一个数字时,你会得到列表中的下一个元素。
您可以完全控制其属性。
答案 5 :(得分:0)
我有类似的任务,通过随机uniq索引从初始切片中选择元素。因此,从具有10k个元素的切片中获得1k个随机uniq元素。
这是简单的解决方案:
import (
"time"
"math/rand"
)
func getRandomElements(array []string) []string {
result := make([]string, 0)
existingIndexes := make(map[int]struct{}, 0)
randomElementsCount = 1000
for i := 0; i < randomElementsCount; i++ {
randomIndex := randomIndex(len(array), existingIndexes)
result = append(result, array[randomIndex])
}
return result
}
func randomIndex(size int, existingIndexes map[int]struct{}) int {
rand.Seed(time.Now().UnixNano())
for {
randomIndex := rand.Intn(size)
_, exists := existingIndexes[randomIndex]
if !exists {
existingIndexes[randomIndex] = struct{}{}
return randomIndex
}
}
}
答案 6 :(得分:0)
您可以使用golang时间包中的UnixNano使用len(12)生成唯一的随机数:
uniqueNumber:=time.Now().UnixNano()/(1<<22)
println(uniqueNumber)
总是随机的:D