我试图在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)
}

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


当前回答

非确定性地为math/rand生成器播种的最好方法是使用hash/ mapaphash (playground):

package main

import (
    "fmt"
    "hash/maphash"
    "math/rand"
)

func main() {
    r := rand.New(rand.NewSource(int64(new(maphash.Hash).Sum64())))
    fmt.Println(r.Int())
}

与time.Now()相比,maphash保证不同的种子(即使在不同的机器上)。与crypto/rand相比,它要快得多,并且是一行程序。

其他回答

如果你的目标只是生成一个随机数刺,那么我认为没有必要用多次函数调用或每次重置种子来复杂化它。

最重要的一步是在实际运行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的原因是它允许简单的字符串格式化。

此外,In rand.Intn(7) Intn作为int返回[0,7)中的非负伪随机数。

随着Go 1.20 (Q4 2022),种子随机数生成器的正确方法也可以是…什么都不做。

如果没有调用Seed,生成器将在程序启动时随机播种。

“math/rand:随机种子全局生成器”提案已被接受(2022年10月),并已开始实施:

CL 443058: math/rand:自动播种全局源代码

实现提案#54880,以自动播种全局源代码。

The justification for this not being a breaking change is that any use of the global source in a package's init function or exported API clearly must be valid - that is, if a package changes how much randomness it consumes at init time or in an exported API, that clearly isn't the kind of breaking change that requires issuing a v2 of that package. That kind of per-package change in the position of the global source is indistinguishable from seeding the global source differently. So if the per-package change is valid, so is auto-seeding. And then, of course, auto-seeding means that packages will be far less likely to depend on the specific results of the global source and therefore not break when those kinds of per-package changes happen in the future. Seed(1) can be called in programs that need the old sequence from the global source and want to restore the old behavior. Of course, those programs will still be broken by the per-package changes just described, and it would be better for them to allocate local sources rather than continue to use the global one.


在issue 20661和CL 436955中,还要注意math/rand。Read已弃用:对于几乎所有用例,crypto/rand。读更合适。

如下所示:

我们可以像这样用gosec linter和golanglint-ci 并注意G404代码: goangci -lint run——disable-all——启用gosec

@[Denys Sé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)
}

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

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

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

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))
}