GO: 随机数

golang有两种伪随机数生成方式:

  • math/rand
  • crypto/rand

math/rand

伪随机数生成器,math/rand包提供了随机数生成的功能。 默认情况下,rand.Int(), rand.Float64()等函数会使用一个固定的种子值,这个种子值在程序启动时就被设定,因此在多个goroutine中并发调用这些函数时可能会产生相同的随机数序列。为了避免这种情况,请使用 Seed(在Go 1.20版本后弃用,新版本请用rand.New(rand.NewSource(seed))代替) 函数设置新的种子。

示例1
package main

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

func main(){
    //版本go1.20之前
	rand.Seed(time.Now().Unix()) // 随机种子 unix 时间戳,秒,
	fmt.Println(rand.Intn(100))
	//版本go1.20+
    r := rand.New(rand.NewSource(time.Now().UnixNano()))//以当前时间纳秒为种子
    fmt.Println(r.Intn(100))
}

如果没有播种随机种子,每次生成结果固定不变

示例2 int float
package main

import (
	"fmt"
	"math/rand"
	"os"
	"text/tabwriter"
	"time"
)

func main(){
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	// The tabwriter here helps us generate aligned output.
	w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
	defer w.Flush()
	show := func(name string, v1, v2, v3 any) {
		fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", name, v1, v2, v3)
	}

	// Float32 and Float64 values are in [0, 1).
	show("Float32", r.Float32(), r.Float32(), r.Float32())
	show("Float64", r.Float64(), r.Float64(), r.Float64())

	// ExpFloat64 values have an average of 1 but decay exponentially.
	show("ExpFloat64", r.ExpFloat64(), r.ExpFloat64(), r.ExpFloat64())

	// NormFloat64 values have an average of 0 and a standard deviation of 1.
	show("NormFloat64", r.NormFloat64(), r.NormFloat64(), r.NormFloat64())

	// Int31, Int63, and Uint32 generate values of the given width.
	// The Int method (not shown) is like either Int31 or Int63
	// depending on the size of 'int'.
	show("Int31", r.Int31(), r.Int31(), r.Int31())
	show("Int63", r.Int63(), r.Int63(), r.Int63())
	show("Uint32", r.Uint32(), r.Uint32(), r.Uint32())

	// Intn, Int31n, and Int63n limit their output to be < n.
	// They do so more carefully than using r.Int()%n.
	show("Intn(10)", r.Intn(10), r.Intn(10), r.Intn(10))
	show("Int31n(10)", r.Int31n(10), r.Int31n(10), r.Int31n(10))
	show("Int63n(10)", r.Int63n(10), r.Int63n(10), r.Int63n(10))

	// Perm generates a random permutation of the numbers [0, n).
	show("Perm", r.Perm(5), r.Perm(5), r.Perm(5))
	
}

输出:

Float32     0.49098578          0.97437197          0.5566082
Float64     0.7360752980379277  0.19502202049691558 0.45157749910091516
ExpFloat64  2.048488925078208   2.661800446853426   0.5394931334128875
NormFloat64 -0.6992690710856053 0.23551531245511903 0.49655457874810127
Int31       610484045           1539528973          9621568
Int63       3916617221603942531 6397125213061450200 5946634199992218543
Uint32      4107348564          1546755905          3803601807
Intn(10)    6                   2                   5
Int31n(10)  8                   0                   4
Int63n(10)  1                   0                   3
Perm        [3 2 1 0 4]         [1 4 0 2 3]         [3 1 0 2 4]

返回一个在[0, 1)范围内的随机浮点数

示例3 取某个范围
package main

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

func main(){
	fmt.Println(RandInt(80,100)) //生成区间随机数
	fmt.Println(GetRandomBoth(4)) //指定字符随机数
	RandShuffle() 打乱字符数组
}
//生成区间随机数
func RandInt(min, max int) int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return r.Intn(max-min) + min
}
//指定字符随机数
func GetRandomBoth(n int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < n; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}
//打乱字符数组
func RandShuffle() {
	words := strings.Fields("ink runs from the corners of my mouth")
	rand.Shuffle(len(words), func(i, j int) {
		words[i], words[j] = words[j], words[i]
	})
	fmt.Println(words)

	// Output:
	// [mouth my the of runs corners from ink]
}

输出:

94
q2IJ

在使用中会遇到的问题

package main

import (
	"fmt"
	"math/rand"
	"time"
)
func main() {
	var i int64
	for i = 0; i < 6; i++ {
		a:= GetRandomBoth(4)
		fmt.Println(a)
	}
}
func GetRandomBoth(n int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < n; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

结果:
nm4u
nm4u
nm4u
nm4u
nm4u
nm4u
出现这种情况的原因是,在同一个for循环,每次操作间隔太短,产生的种子值(纳秒)相同,产生的随机序列也相同,每次取的同一个位置值,所以每次取的随机数相同。
解决办法将设置种子代码

r := rand.New(rand.NewSource(time.Now().UnixNano()))

放在公共位置,例如init函数。
示例如下

package main

import (
	"fmt"
	"math/rand"
	"time"
)
var RandNew *rand.Rand
func init()  {
	RandNew=rand.New(rand.NewSource(time.Now().UnixNano()))
}
func GetRandomBoth(n int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	for i := 0; i < n; i++ {
		result = append(result, bytes[RandNew.Intn(len(bytes))])
	}
	return string(result)
}
func main() {
	var i int64
	for i = 0; i < 6; i++ {
		a:= GetRandomBoth(4)
		fmt.Println(a)
	}
}

结果:
1Ezg
FIFD
FKOU
VgBo
7PiE
k7xG

crypto/rand

实现了一个密码安全的强伪随机数生成器
Reader是一个密码强大的伪随机生成器的全球共享实例。
在Linux上,Reader 使用 getrandom(2)(如果可用),否则使用 /dev/urandom。在OpenBSD 上,Reader使用 getentropy(2)。在其他类 Unix 系统上,Reader 从 /dev/urandom 读取。在 Windows 系统上,Reader 使用 CryptGenRandom API。

package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
)

func main(){
	for i := 0; i < 4; i++  {
		n, _ := rand.Int(rand.Reader, big.NewInt(100))
		println(n.Int64())
	}

	c := 10
	b := make([]byte, c)
	//if _, err := io.ReadFull(rand.Reader, b); err != nil {
	//	return
	//}
	//或
	_, err := rand.Read(b)
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println(b)
}


总结

crypto/rand包是一个可以应用在安全系统中的强伪随机数生成器,因为无法从过去的伪随机数序列中推测出下一个伪随机数,但执行效率没有math/rand高

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易风有点疯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值