随机种子

前言

从入职到现在一直在使用随机数,只知道在使用随机数时,需要先设置一下随机种子,但却不知道为什么要这么做,知其然不知其所以然,今天刚好又用到了,然后就想彻底搞明白

伪随机数

其实我们现在很多地方使用的随机数都是伪随机数,为什么这么说呢?因为产生的随机数都是有规律的,并不是真正的随机(无规律),是通过算法产生的,跟传入的随机种子有关(随机种子稍后会说)。如果传入的随机种子相同,则每次产生的随机数也相同:

--lua
math.randomseed(6)
for i=1,10 do
    local randomNum = math.random()
    print(randomNum)
end

产生的输出如下:


随机种子相同

多次运行,可以看到,每次运行的结果都相同。(注:单次运行,产生10个的随机数是不相同的,但每次运行,都是同样这10个不同随机数,这显然不是我们想要的结果)

随机种子

上面可以知道,我们使用的大多数random函数都是伪随机函数,那怎么才能做到每次运行产生的随机数不同?这就要使用到随机种子。什么是随机种子?伪随机数是由算法产生的,随机种子就是给这个算法提供一个初始参数。以线性同余发生器为例:

线性同余发生器

这个说的有点绕,其实简化了就是下面的公式:

RAND_SEED=(RAND_SEED*123+59)%65536;

解释一下:你传入的随机种子会被当做该算法的初始参数,也就是上面的RAND_SEED,也就是用这个参数乘以一个常量123,再加上一个常量59,然后用一个很多大的数65536取余,得到第一个随机数。然后把生成的第一个随机数再次当做参数,生成第二个随机数...依次类推,得到所需的所有随机数。
这就是为什么随机种子一样,每次运行得到的随机数也是一样的

随机种子选取

随机种子的选取,对伪随机数的随机性至关重要,每次运行的随机种子一定要不同,所以在很多地方你都可以看到用当前的时间作为随机种子,这已经满足了大多数需求。但是这也有弊端,还是以lua为例来说明:

math.randomseed(os.time())
function testRandom()
  for i=1, 5 do
     print(math.random())
  end
end

testRandom()

虽然我们使用了时间作为随机种子,但如果两次运行的时间很接近,得到的随机数还是很相似甚至相同,因为os.time()返回的秒级的时间,时间粒度不够精细。
那怎么才能避免这种情况呢?可以使用下面的方法,获取时间的后六位,并做倒置,这样即使时间变化不大,也能产生差别很大的随机数:

math.randomseed(tostring(os.time()):reverse():sub(1, 6))

总结

综上,现在可以理解,为什么随机种子称为“种子”,因为真的很像一颗“种子”,不同“种子”可以生成不同的随机“树”,相同的“种子”生成的随机“树”也是一样的,是不是很形象!
所以在使用时,要注意一下两点:

  1. 程序启动时,只需要设置一次随机种子即可(除非你真的知道你在干什么)
  2. 每次程序启动,随机种子要选取不一样,当前时间作为随机种子是个好方法,但有可能因为精度不够,造成随机数不够随机,因此可以使用精度更高的时间并且倒置该数。

你可能感兴趣的:(随机种子)