随机数(四)

游戏开发过程中,有大量的随机数需求。本文将着重介绍go的随机数用法(伪随机)。

原理:

种子决定了接下来的随机数值,直到种子变化!

相同种子的情况下得到的随机数值是必然一样的。

一旦错误的使用了种子,为什么会出现连续出现相同数的情况。在rand包中默认会有一个种子数为0的随机数生成器。首先看一下下面的代码。

错误用法一:

for i:=0;i<10;i++{
    rand.Seed(time.Now().UnixNano())
    x:=rand.Int()
}

上面的错误使用例子中,x很大程度上将是相同的数。因连续10次设置了相同的种子。开头原理部分已经知道为何会得到这样的结果,下面通过另外一个方式再描述:

当我们调用rand.Seed(种子)之后,立即就会得到一个将来随机出来的数组。

[数1、数2、数3、...数n]

每当调用Seed后,根据种子不同,数组不同。并且会将下一次取出的随机数下表设置到0。

我们改进用法如下:(错误用法二

rand.Seed(time.Now().UnixNano())
for i:=0;i<10;i++{
    x:=rand.Int()
}

上面代码中,我们将得到连续10个随机数,且他们都是有效的。但这依然是错误的用法。因为在并发情况下多处调用rand.Seed实际上设置的是同一个生成器的种子(这样必然可能导致反复设置到相同的种子情况存在)。下面是正确创建一个随机数生成器的代码。

rand.New(rand.NewSource(种子))

将错误方法二中的rand.替换为手动创建的独立生成器即可根治该问题。

实际生产举例:

与u3d的C#对接

客户端计算,服务端验证的情况下,客户端保持后后端同样的种子算法。在验证时提交使用的种子,服务端使用相同的种子计算(则可以完整还原客户端环境下的所有数值情况)

下面是封装之后的随机数函数(在全局非验证情况下,可以直接调用Rand函数,无需担心种子问题)

package util

import (
	"math/rand"
	"sync/atomic"
	"time"
)

//@ return nil
func NewRand(seed int64) *rand.Rand {
	return rand.New(rand.NewSource(seed))
}

var _seedSec int64 = 0

func seed() {
	s := time.Now().Unix()
	old := atomic.SwapInt64(&_seedSec, s)
	if old != s { //对比先后时间,若超过1秒,重新用纳秒重置种子
		rand.Seed(time.Now().UnixNano())
	}
}

//@return 0
func RandInt() int {
	seed()
	return rand.Int()
}

//@return 0
func RandIntn(n int) int {
	seed()
	return rand.Intn(n)
}

//@return 0
func RandInt31() int32 {
	seed()
	return rand.Int31()
}

//@return 0
func RandInt31n(n int32) int32 {
	seed()
	return rand.Int31n(n)
}

func RandInt63() int64 {
	seed()
	return rand.Int63()
}

//@return 0
func RandInt63n(n int64) int64 {
	seed()
	return rand.Int63n(n)
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值