JS生成相同的随机数(伪随机数)

尊重博主版权,该博客引用了以下博客内容:详谈JS中实现种子随机数及作用

最近开发遇到了一个新功能,要求做到相同随机数。何为相同随机数,即每次使用相同的随机种子,产生相同的数字。在网上找了一下,只找到上面一篇博客,所以,这里为了让更多的人了解,博主决定自己写一篇博客,将原博客内容引用过来,让更多的人能够了解到这一内容。

其实,我们平时在开发过程中,经常会遇到随机数问题,例如,随机抽奖,微信飞机大战中,随机产生敌人位置等等。但实际上这些都是伪随机,用C语言开发的时候,使用random函数的时候,会发现,当我们直接调用这个方法的时候,每次运行都产生相同的随机数,所以,在调用这个方法的时候,都会用时间来做随机种子。当然,在JS中直接调用Math.random方法,就能直接产生随机数,这里不需要设置时间种子,JS底层已经为我们设置了随机种子,而且,每次是不一样的。既然是伪随机,那么,我们可以根据这个特性,每次设置相同的随机数,每次运行的时候,都产生相同的随机数。


Math.seed = 5; 
Math.seededRandom = function(max, min) { 
    max = max || 1;
    min = min || 0; 
    Math.seed = (Math.seed * 9301 + 49297) % 233280; 
    var rnd = Math.seed / 233280.0;
    return min + rnd * (max - min); 
};
for (var i= 0; i<10; i++) {
     console.log(Math.seededRandom()); 
}

这里的Math.seed是自己定义的一个变量,Math.seededRandom也是自己定义的一个方法,如果不喜欢这种方式,也可以定义成其他的名字。下面,引用原博主的内容:

运行如上代码你会发现如果种子Math.seed不变,那么生成的随机数是不会变化的,哦了,如果引入这个函数,那么重现游戏场景可以实现了,虽然还需要做更多的细节处理,但机制上是能保证的,本文的重点不是实现一个这样的游戏。

本文的重点是:(Math.seed * 9301 + 49297) % 233280,为什么会是这三个值,而不是其它的到底这三个数字有什么神秘的

来历呢?

像Math.seededRandom这种伪随机数生成器叫做线性同余生成器(LCG, Linear Congruential Generator),几乎所有的运行库

提供的rand都是采用的LCG,形如:

I n+1=aI n+c(mod m)

生成的伪随机数序列最大周期m,范围在0到m-1之间。要达到这个最大周期,必须满足:
1.c与m互质

2.a - 1可以被m的所有质因数整除

3.如果m是4的倍数,a - 1也必须是4的倍数

以上三条被称为Hull-Dobell定理。作为一个伪随机数生成器,周期不够大是不好意思混的,所以这是要求之一。因此才有了:

a=9301, c = 49297, m = 233280这组参数,以上三条全部满足。

下面,我们来继续讨论刚刚上面的恢复敌人位置的问题。现在我们可以自己产生随机数了,而且,按照我们的要

求,可以每次都产生相同的随机数了。也就是说,我们可以每次随机敌人的位置了。

这时有人会问,为什么要每次产生相同的随机数呢?其实,这里有很大的用处,例如,我们在玩一个游戏的时候,随机产生了几个敌人的位置,这个时候,如果网络不好,重新连接进来,会发现,敌人的位置变了,此时,玩家看起来,就会发小屏幕上敌人位置发生了跳动,从玩家体验的角度来说,这是一种很差的体验。有人会说,为什么不在恢复现场的时候,直接将敌人的位置全部发送回来呢?下面,我来举一个简单的例子,来说明这个问题:

如果我们将敌人定义为1~10种类型,那么,正常情况下,如果我们为了在重新进入游戏的时候,继续上面的进度进行游戏,我们会将敌人类型和敌人位置都传回来,此时,我们传回来的数据格式,我用JSON简单来表示,大概应该是这种:


{
    'enemyList':[{
        'enemyType':1,
        'x':33,
        'y':25
    },{
        'enemyType':2,
        'x':45,
        'y':67
    },{
        'enemyType':3,
        'x':84,
        'y':12
    }]
}

那么,如果我们需要在服务器记录所有敌人的位置,如果用户量比较大,其实这数据量就挺大的了,而且,在重新进入游戏的时候,其实传回来的数据也是不少的。那下面,我们换一种思路,这次,我们每次计算玩家的位置都用随机数来计算,然后,当我们重新进入游戏的时候,服务器将相同的随机数种子发送到客户端,让服务器自己计算位置,同时将敌人的类型发回来,大概的数据格式如下:


{
    'seed': 12345,
    'enemyList':[1,2,3]
}

有没有觉得很精简,这样,不仅服务器储存的数据量大大减少,而且,服务器给客户端发包的时候,数据量也较少了好多,差不多是之前的1/3,这在开发中,真的好处很多的,玩家网络不好,中间网络断了,重新连接,可以恢复玩家当前的场景,网络不好,重连的时候,数据量减少了好多,如果用第一种办法,那重连了数据量反倒挺大的,这并不符合开发的要求啊,所以,在这里,随机数就真的起了大的作用。

以上就是产生相同随机数的方法,如果有什么描述不清楚地地方,欢迎大家留言交流!




你可能感兴趣的:(游戏开发,随机数)