尽管完全随机是不可能的,我们仍然可以在计算机上使用伪随机数。
我们可以有常规的伪随机数,以及影像学上安全的伪随机数。
让我们看看如何在Go中做到这一点。
伪随机数
这math/rand
包裹Go标准库提供的内容使我们伪随机数生成器(PRNG), 也被称为确定性随机位发生器。
与所有伪数字生成器一样,通过math/rand
是不是很随机默认情况下是确定性的每次都会打印相同的值。
例如,尝试运行此代码,其中介绍了rand.Intn(n)
,返回介于之间的随机数0
和n - 1
。
package main
import (
“fmt”
“math/rand”
)
func main() {
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
}
每次运行该程序时,您总是会看到相同的顺序。随机数在程序内部会发生变化,但是每次运行它时,您都会得到相同的输出:
11
27
17
29
1
这是因为默认情况下,种子始终是相同的,即数字1
。要实际获得一个随机数,您需要提供一个唯一的种子为您的程序。您确实不希望忘记播种,而是正确地为我们的伪数生成器播种。如何?
使用rand.Seed()
在打电话给任何人之前math/rand
方法,通过int64
价值。您只需要在程序中播种一次,而不是每次都需要一个随机数。最常用的种子是当前时间,转换为int64
经过Unix纳米和rand.Seed(time.Now().UnixNano())
:
package main
import (
“fmt”
“math/rand”
“time”
)
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
fmt.Println(rand.Intn(30))
}
请记住,由于其沙箱操作,Go Playground总是在同一时间开始,因此此代码无法按预期工作。在实际环境中进行尝试,并且以前没有改变的数字现在每次运行该程序时,它们的打印方式都会有所不同。
下面列出了一些常见示例,以方便重用:
产生一个随机整数
package main
import (
“fmt”
“math/rand”
“time”
)
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randomInt(1, 11)) //get an int in the 1…10 range
}
产生随机字串
package main
import (
“fmt”
“math/rand”
“time”
)
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randomString(10)) // print 10 chars
}
将以大写格式返回10个字符。改变
bytes[i] = byte(randomInt(65, 90))
到
bytes[i] = byte(randomInt(97, 122))
仅小写。
如果您想从中选择特定的字符池,请使用
package main
import (
“fmt”
“math/rand”
“time”
)
var pool = “_:$%&/()”
// Generate a random string of A-Z chars with len = l
func randomString(l int) string {
bytes := make([]byte, l)
for i := 0; i < l; i++ {
bytes[i] = pool[rand.Intn(len(pool))]
}
return string(bytes)
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randomString(1000))
}
改变len(pool)
到utf8.RuneCountInString(pool)
如果您使用非ascii字符串,则为len()
计算字节数,但并非所有字符都只占用Unicode中的一个字节-请参见https://stackoverflow.com/questions/12668681/how-to-get-the-number-of-characters-in-a-string。
生成随机整数数组
package main
import (
“fmt”
“math/rand”
“time”
)
func randomArray(len int) []int {
a := make([]int, len)
for i := 0; i <= len-1; i++ {
a[i] = rand.Intn(len)
}
return a
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randomArray(10))
}
加密级随机数
去还提供了加密安全的伪随机数生成器(CSPRNG)在标准库包中crypto.rand
所以您可能会问,为什么我还要使用由提供的伪数随机生成器库?math/rand
反而?好吧,这取决于用例。math/rand
是快多了适用于不需要加密级别或与安全性相关的随机数据生成的应用程序。crypto.rand
适用于安全且可加密的用途,但速度较慢。
它应该用于什么?例如,生成密码,CSRF令牌,会话密钥或与安全性远程相关的任何内容。
它不依赖于当前时间,就像我们在前面的示例中所做的那样math/rand
,而是使用操作系统CSPRNG API:Windows上的CryptGenRandom API和/dev/urandom/
在所有其他版本(Linux,OSX,* nix)上
您直接获得256个随机字节
package main
import (
“crypto/rand”
“fmt”
)
func main() {
key := [256]byte{}
_, err := rand.Read(key[:])
if err != nil {
panic(err)
}
fmt.Println(key)
}
我正在从中获取代码示例马特·西尔弗洛克(Matt Silverlock):您可以使其更通用,并创建一个随机字节生成函数
// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">b</span>, <span style="color:#66d9ef">nil</span>
}
并使用它,一个随机字符串生成函数,
// GenerateRandomString returns a URL-safe, base64 encoded
// securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomString(s int) (string, error) {
b, err := GenerateRandomBytes(s)
return base64.URLEncoding.EncodeToString(b), err
}
更多教程:
- 使用NGINX反向代理服务Go服务
- 在Go中复制结构
- Go Web服务器的基础
- 在Go中对地图类型进行排序
- 简而言之去指针
- 转到标签说明
- 开始日期和时间格式
- 使用Go进行JSON处理
- 可变参数函数
- 去弦备忘单
- 转到空界面说明
- 使用VS Code和Delve调试Go
- 命名为Go返回参数
- 在Go中生成随机数和字符串
- Go项目的文件系统结构
- Go中的二进制搜索算法
- 在Go中使用命令行标志
- GOPATH解释
- 使用Go构建一个命令行应用程序:lolcat
- 使用Go构建CLI命令:Cowsay
- 在Go中使用壳管
- Go CLI教程:财富克隆
- 使用Go列出文件夹中的文件
- 使用Go从GitHub获取存储库列表
- 去,将一小段字符串附加到文件中
- 去,将字符串转换为字节片
- 使用Go可视化您本地的Git贡献
- Go CPU和内存分析入门
- 解决Go程序中的“不支持索引”错误
- 测量Go程序中的执行时间
- 使用Go构建Web爬网程序以检测重复的标题
- 最佳实践:指针还是价值接收者?
- 最佳实践:您应该使用方法还是函数?
- Go数据结构:集
- 前往地图备忘单
- 在Go中生成泛型类型的实现
- Go数据结构:字典
- Go数据结构:哈希表
- 在“通过通道”中实现事件侦听器
- Go数据结构:堆栈
- Go数据结构:队列
- Go数据结构:二进制搜索树
- Go数据结构:图形
- Go数据结构:链表
- Go数据结构的完整指南
- 比较Go值
- Go是面向对象的吗?
- 在Go中使用SQL数据库
- 在Go中使用环境变量
- 上篇教程:PostgreSQL支持的REST API
- 在Go Web服务器上启用CORS
- 在Docker容器中部署Go应用程序
- 为什么Go是作为PHP开发人员学习的功能强大的语言
- 去,删除io.Reader.ReadString换行符
- 开始,如何观看更改并重建程序
- 去算一下自约会以来的月份
- 在Go中访问HTTP POST参数