如何使用标准库在Go中生成*唯一*随机数

时间:2016-10-07 21:04:49

标签: random go

问题:如何在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程序进行基准测试。

7 个答案:

答案 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