Golang随机数生成器如何正确播种

时间:2012-09-07 15:29:44

标签: random go

我想在Go中生成一个随机字符串,这是我到目前为止编写的代码:

package main

import (
    "bytes"
    "fmt"
    "math/rand"
    "time"
)

func main() {
    fmt.Println(randomString(10))
}

func randomString(l int) string {
    var result bytes.Buffer
    var temp string
    for i := 0; i < l; {
        if string(randInt(65, 90)) != temp {
            temp = string(randInt(65, 90))
            result.WriteString(temp)
            i++
        }
    }
    return result.String()
}

func randInt(min int, max int) int {
    rand.Seed(time.Now().UTC().UnixNano())
    return min + rand.Intn(max-min)
}

我的实施很慢。使用time播种会在一定时间内带来相同的随机数,因此循环会一次又一次地迭代。如何改进我的代码?

10 个答案:

答案 0 :(得分:201)

每次设置相同的种子时,都会得到相同的序列。所以当然如果你将种子设置为快速循环中的时间,你可能会多次使用相同的种子调用它。

在你的情况下,当你调用randInt函数直到你有不同的值时,你正在等待时间(由Nano返回)来改变。

As for all pseudo-random libraries,你必须只设置种子一次,例如在初始化你的程序时,除非你特别需要重现给定的序列(通常只进行调试和单元测试)。

之后,您只需调用Intn即可获得下一个随机整数。

rand.Seed(time.Now().UTC().UnixNano())行从randInt函数移动到main的开头,一切都会更快。

另请注意,我认为您可以简化字符串构建:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    fmt.Println(randomString(10))
}

func randomString(l int) string {
    bytes := make([]byte, l)
    for i := 0; i < l; i++ {
        bytes[i] = byte(randInt(65, 90))
    }
    return string(bytes)
}

func randInt(min int, max int) int {
    return min + rand.Intn(max-min)
}

答案 1 :(得分:15)

只是为了后代而抛弃它:有时候最好使用初始字符集字符串生成随机字符串。如果字符串应该由人手动输入,这很有用;排除0,O,1和l可以帮助减少用户错误。

var alpha = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"

// generates a random string of fixed size
func srand(size int) string {
    buf := make([]byte, size)
    for i := 0; i < size; i++ {
        buf[i] = alpha[rand.Intn(len(alpha))]
    }
    return string(buf)
}

我通常将种子设置在init()块内。他们在此处记录:http://golang.org/doc/effective_go.html#init

答案 2 :(得分:12)

好的,为什么这么复杂!

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed( time.Now().UnixNano())
    var bytes int

    for i:= 0 ; i < 10 ; i++{ 
        bytes = rand.Intn(6)+1
        fmt.Println(bytes)
        }
    //fmt.Println(time.Now().UnixNano())
}

这是基于dystroy的代码,但符合我的需要。

它死了六次(rands int 1 =< i =< 6

func randomInt (min int , max int  ) int {
    var bytes int
    bytes = min + rand.Intn(max)
    return int(bytes)
}

上述功能完全相同。

我希望这些信息有用。

答案 3 :(得分:10)

我不明白为什么人们播种时间价值。以我的经验,这从来不是一个好主意。例如,虽然系统时钟可能以纳秒为单位表示,但系统的时钟精度不是纳秒。

This program不应在Go游乐场上运行,但如果在计算机上运行,​​则可以粗略估算出您可以期望的精度类型。我看到约1000000 ns的增量,所以1毫秒的增量。那是20位未使用的熵。 一直以来,高位基本上都是恒定的。

这对您重要的程度会有所不同,但您只需使用crypto/rand.Read作为种子的来源,就可以避免基于时钟的种子值的陷阱。它将为您提供您可能会在随机数中寻找的不确定性质量(即使实际实现本身仅限于一组不同且确定性的随机序列)。

import (
    crypto_rand "crypto/rand"
    "encoding/binary"
    math_rand "math/rand"
)

func init() {
    var b [8]byte
    _, err := crypto_rand.Read(b[:])
    if err != nil {
        panic("cannot seed math/rand package with cryptographically secure random number generator")
    }
    math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
}

作为旁注,但与您的问题有关。您可以使用此方法创建自己的rand.Source,以避免使用锁来保护源代码的开销。 rand软件包实用程序函数很方便,但它们也使用幕后的锁来防止同时使用源。如果不需要,可以通过创建自己的Source并以非并行方式使用它来避免这种情况。无论如何,您不应该在迭代之间重新使用随机数生成器,它永远不会被设计为以这种方式使用。

答案 4 :(得分:1)

我尝试了下面的程序,每次都看到不同的字符串

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func RandomString(count int){
  rand.Seed(time.Now().UTC().UnixNano()) 
  for(count > 0 ){
    x := Random(65,91)
    fmt.Printf("%c",x)
    count--;
  }
}

func Random(min, max int) (int){
 return min+rand.Intn(max-min) 
}

func main() {
 RandomString(12)
}

控制台上的输出是

D:\james\work\gox>go run rand.go
JFBYKAPEBCRC
D:\james\work\gox>go run rand.go
VDUEBIIDFQIB
D:\james\work\gox>go run rand.go
VJYDQPVGRPXM

答案 5 :(得分:0)

这是纳秒,两次获得相同种子的几率是多少 无论如何,感谢您的帮助,这是基于所有输入的最终解决方案。

package main

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UTC().UnixNano())
}

// generates a random string
func srand(min, max int, readable bool) string {

    var length int
    var char string

    if min < max {
        length = min + rand.Intn(max-min)
    } else {
        length = min
    }

    if readable == false {
        char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    } else {
        char = "ABCDEFHJLMNQRTUVWXYZabcefghijkmnopqrtuvwxyz23479"
    }

    buf := make([]byte, length)
    for i := 0; i < length; i++ {
        buf[i] = char[rand.Intn(len(char)-1)]
    }
    return string(buf)
}

// For testing only
func main() {
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, false))
    println(srand(5, 7, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 4, true))
    println(srand(5, 400, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
}

答案 6 :(得分:0)

如果您的目的只是生成一个随机数的字符串,那么我认为没有必要将它与多个函数调用复杂化或每次都重置种子。

最重要的步骤是在实际运行rand.Init(x)之前调用一次种子函数。 Seed使用提供的种子值将默认Source初始化为确定性状态。因此,建议在实际函数调用伪随机数生成器之前先调用它。

下面是创建随机数字符串的示例代码

package main 
import (
    "fmt"
    "math/rand"
    "time"
)



func main(){
    rand.Seed(time.Now().UnixNano())

    var s string
    for i:=0;i<10;i++{
    s+=fmt.Sprintf("%d ",rand.Intn(7))
    }
    fmt.Printf(s)
}

我使用Sprintf的原因是因为它允许简单的字符串格式。

此外,在rand.Intn(7)中,Intn以int形式返回[0,7)中的非负伪随机数。

答案 7 :(得分:0)

由于golang api的更改而导致的小更新,请省略.UTC():

time.Now()。 UTC() .UnixNano()-> time.Now()。UnixNano()

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(randomInt(100, 1000))
}

func randInt(min int, max int) int {
    return min + rand.Intn(max-min)
}

答案 8 :(得分:0)

@ [DenysSéguret]已正确发布。但就我而言,我每次都需要新的种子,因此代码如下;

如果您需要快速功能。我这样使用。


func RandInt(min, max int) int {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return r.Intn(max-min) + min
}

func RandFloat(min, max float64) float64 {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return min + r.Float64()*(max-min)
}

source

答案 9 :(得分:0)

每次在 for 循环内调用 randint() 方法时,都会设置不同的种子,并根据 时间 生成序列。但是由于循环在您的计算机中在短时间内运行得很快,种子几乎相同,并且由于时间,生成了与过去非常相似的序列。所以在 randint() 方法之外设置种子就足够了。

package main

import (
    "bytes"
    "fmt"
    "math/rand"
    "time"
)

var r = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
func main() {
    fmt.Println(randomString(10))
}

func randomString(l int) string {

    var result bytes.Buffer
    var temp string
    for i := 0; i < l; {
        if string(randInt(65, 90)) != temp {
            temp = string(randInt(65, 90))
            result.WriteString(temp)
            i++
        }
    }
    return result.String()
}

func randInt(min int, max int) int {
    return min + r.Intn(max-min)
}