我试图在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)
}
Run Code Online (Sandbox Code Playgroud)
我的实施很慢.播种使用time在一定时间内带来相同的随机数,因此循环一次又一次地迭代.我该如何改进我的代码?
Den*_*ret 213
每次设置相同的种子时,都会得到相同的序列.因此,当然如果您将种子设置为快速循环中的时间,您可能会多次使用相同的种子调用它.
在你的情况下,当你调用你的randInt函数直到你有不同的值时,你正在等待时间(由Nano返回)来改变.
对于所有伪随机库,您必须只设置一次种子,例如在初始化程序时,除非您特别需要重现给定的序列(通常仅用于调试和单元测试).
之后,您只需调用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)
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*ren 21
我不明白为什么人们在播种时间价值。以我的经验,这从来不是一个好主意。例如,虽然系统时钟可能以纳秒表示,但系统的时钟精度不是纳秒。
该程序不应在Go操场上运行,但是如果您在计算机上运行该程序,则可以粗略估算出可以期望的精度类型。我看到约1000000 ns的增量,所以1毫秒的增量。那是20位未使用的熵。一直以来,高位大多是恒定的。
对您而言重要的程度会有所不同,但是您可以通过简单地将crypto/rand.Readas用作种子的源来避免基于时钟的种子值的陷阱。它将为您提供您可能会在随机数中寻找的不确定性质量(即使实际实现本身仅限于一组不同且确定性的随机序列)。
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[:])))
}
Run Code Online (Sandbox Code Playgroud)
作为附带说明,但与您的问题有关。您可以rand.Source使用此方法创建自己的方法,以避免使用锁来保护源代码的开销。所述rand包效用函数是便利的,但它们也使用锁罩下以防止源被同时使用。如果不需要,可以通过创建自己的方法Source并以非并行方式使用它来避免这种情况。无论如何,您不应该在迭代之间重新使用随机数生成器,它永远不会被设计为以这种方式使用。
jor*_*lli 16
只是为了后代而抛弃它:有时候最好使用初始字符集字符串生成随机字符串.如果字符串应该由人手动输入,这很有用; 排除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)
}
Run Code Online (Sandbox Code Playgroud)
我通常将种子设置在一个init()块内.它们在此处记录:http://golang.org/doc/effective_go.html#init
Von*_*onC 16
对于 Go 1.20(2022 年第 4 季度),播种随机数生成器的正确方法也可能是……什么也不做。
如果Seed未调用,生成器将在程序启动时随机播种。
提案“ math/rand:随机生成全局种子”被接受(2022 年 10 月),并且已经开始实施:
math/rand: 自动种子全局源实施提案#54880,自动播种全局源。
这不是重大更改的理由是,在包的
init函数或导出的 API 中对全局源的任何使用显然都必须有效 - 也就是说,如果包改变了它在某个时间或在导出的 API 中消耗的随机性init,那么显然不是那种需要发布该软件包 v2 的重大更改。
这种按包改变全局源位置与以不同方式播种全局源没有什么区别。因此,如果每个包的更改有效,那么自动播种也是有效的。当然,自动播种意味着包将不太可能依赖于全局源的特定结果,因此当将来发生此类每个包的更改时也不会中断。
Seed(1)可以在需要全局源旧序列并想要恢复旧行为的程序中调用。
当然,这些程序仍然会被刚才描述的每个包的更改所破坏,并且它们最好分配本地源而不是继续使用全局源。
从问题 20661和CL 436955中,还请注意math/rand.Read已弃用:对于几乎所有用例,crypto/rand.Read更合适。
正如这里所指出的:
可以像这样使用
goseclintergolanglint-ci并观察G404代码:Run Code Online (Sandbox Code Playgroud)golangci-lint run --disable-all --enable gosec
小智 14
好吧为什么这么复杂!
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())
}
Run Code Online (Sandbox Code Playgroud)
这是基于dystroy的代码,但符合我的需要.
它死了六个(rands int 1 =< i =< 6)
func randomInt (min int , max int ) int {
var bytes int
bytes = min + rand.Intn(max)
return int(bytes)
}
Run Code Online (Sandbox Code Playgroud)
上面的功能完全相同.
我希望这些信息有用.
小智 10
不确定地为生成器提供种子的最佳方法math/rand是使用hash/maphash( 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())
}
Run Code Online (Sandbox Code Playgroud)
与 相比time.Now(),maphash保证了不同的种子(即使在不同的机器上)。与 相比crypto/rand,它要快得多,并且是一个单行代码。