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

Random 如何使用标准库在Go中生成*唯一*随机数,random,go,Random,Go,问题:如何在Go中生成唯一的随机数流 也就是说,我想使用math/rand和/或标准Go库实用程序保证数组a中没有重复 func RandomNumberGenerator() *rand.Rand { s1 := rand.NewSource(time.Now().UnixNano()) r1 := rand.New(s1) return r1 } rng := RandomNumberGenerator() N := 10000 for i

问题:如何在Go中生成唯一的随机数流

也就是说,我想使用
math/rand
和/或标准Go库实用程序保证数组
a
中没有重复

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()
}
func RandomNumberGenerator()*rand.rand{
s1:=rand.NewSource(time.Now().UnixNano())
r1:=新兰特(s1)
返回r1
}
rng:=RandomNumberGenerator()
N:=10000
对于i:=0;i

例如,关于如何在Go中生成一系列随机数,存在一些问题和解决方案

但现在我想生成一系列随机数,这些随机数不会与前面的值重复。在围棋中有没有标准/推荐的方法

我的猜测是(1)使用置换或(2)跟踪以前生成的数字,如果以前生成过,则重新生成一个值

但解决方案(1)如果我只想要几个数字,听起来像是一个杀伤力过大的解决方案(2)如果我因为碰撞而生成了一长串随机数,听起来非常耗时,我想这也非常消耗内存



用例:用10K、100K、1M无重复的伪随机数对Go程序进行基准测试。

您绝对应该使用方法2。假设您在64位机器上运行,因此生成63位整数(64位,但
rand.Int
从不返回负数)。即使你生成了40亿个数字,任何给定数字复制的可能性仍然只有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-:


2-只需填写
映射[int32]结构{}

for i := int32(0); i < n; i++ {
        m[i] = zero
}
对于1000000个唯一的数字,这只需要183ms,没有重复():

主程序包
进口(
“fmt”
“时间”
)
func main(){
常数n=1000000
m:=make(映射[int32]结构{},n)
t:=时间。现在()
对于i:=int32(0);i

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))
}
主程序包
导入“时间”
输入“fmt”
导入“数学/兰德”
func main(){
dup:=0
t:=时间。现在()
常数n=200000
rand.Seed(time.Now().UTC().UnixNano())
变量a[n]int32
变量存在布尔
对于i:=0;i
基于@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
    }
}
func TestSetGet2(t*testing.t){
常数N=10000
对于33;,掩码:=范围[]int{0,-1,0x555555,0xAAAAA,0x333333,0xCCCC,0x314159}{
rng:=NewUniqueRand(2*N)
a:=make([]整数,N)
对于i:=0;ia[i]=(rng.Int()^mask)我认为有两个原因需要这样做。您想要测试随机数生成器,或者想要唯一的随机数

你在测试一个随机数发生器 我的第一个问题是为什么?有很多可靠的随机数生成器可用。不要自己写,它基本上只是涉猎密码学,这从来不是一个好主意。也许你正在测试一个使用随机数生成器生成随机输出的系统

有一个问题:无法保证随机数是唯一的。它们是随机的。总是有冲突的可能性。测试随机输出是唯一的是不正确的

相反,您要测试的结果是均匀分布的

你想要唯一的随机数 从实际的角度来看,您不需要保证唯一性,但要使冲突不太可能发生,这不是一个问题。这就是原因。它们是128位通用唯一标识符。有许多方法可以为特定场景生成它们

UUIDv4基本上只是一个122位的随机数,它发生冲突的可能性非常小

解决n

n = sqrt(2MP)
将p设为1e-12(万亿分之一)之类的荒谬值,我们发现可以生成约3.2万亿个UUIDV4,碰撞概率为万亿分之一。您中彩票的概率是3.2万亿个UUIDV4碰撞概率的1000倍。我认为这是可以接受的

下面是生成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
    }
}
主程序包
进口(
“fmt”
“github.com/frankenbeanies/uuid4”
)
func main(){

对于i:=0;i我正在电话上键入此内容,因此请原谅代码缺失或格式错误

我会这样做:

生成有序唯一编号的列表

选择任意两个随机索引并交换其元素

继续交换一定数量的迭代

剩下的部分是您的随机唯一列表

注:

它很简单,内存使用与大小成正比

该列表可以随时生成和随机化,甚至可以使用go generate进行预编译

当你想要一个数字时,你会得到列表中的下一个元素


您可以完全控制它的属性。

我有类似的任务,通过随机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
        }
    }
}
导入(
“时间”
“数学/兰德”
)
func getRandomElements(数组[]字符串)[]字符串{
结果:
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
    }
}
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 = sqrt(2MP)
package main

import (
    "fmt"
    "github.com/frankenbeanies/uuid4"
)

func main() {
    for i := 0; i <= 1000000; i++ {
        uuid := uuid4.New().Bytes()

        // use the uuid
    }
}
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
        }
    }
}
uniqueNumber:=time.Now().UnixNano()/(1<<22)
println(uniqueNumber)