当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 >
只要运气足够好,一刀也能999!JAVA随机数快速入门
时间:2018-12-24作者:华清远见

“随机数”的本质代表了人类的不可预知性,这一特性在编程领域可以说是必不可少的。需要随机数发挥关键性作用的例子几乎随处可见:游戏中的数值浮动、抽奖系统中的号码生成、安全领域的秘钥生成、服务器集群中的负载均衡……这些或大或小、或简单或复杂的功能,都需要基于随机数实现。

所以,本文的目的就是要用最简单易懂的方式来介绍如何在Java中使用随机数。

不过,在开始之前,首先需要明确的一点是:本文中介绍的Java自带API生成的随机数都是“伪随机数”——生成它们的生成算法是一种叫做“线性同余”的算法,它接收一个数字作为种子,根据该种子输出一个看似随机的数字。这种算法的输出虽然看似“随机”,不过它的结果其实是有周期的(只是周期很长),而数论的知识又告诉我们:有周期的东西一定可以预言。这就宣判了Java API生成的随机数并不是真正“随机”的随机数,其生成结果其实取决于种子的数值,换句话说,使用相同种子生成的“随机数”一定相同,无论试多少次。

听到这里,可能会有不少初学者对其表示失望。不过别急着走,实际上我们只需每次都找一个不同的种子,那么也能够令其生成的结果看起来无限接近于“随机”。

那么如何才能找一个“每次都不一样”的种子呢?需要注意的是,种子可以是有规律的——这也是随机数生成函数存在的意义(否则还要它干嘛)。这样的“种子”在现实生活中或者程序中几乎随处可见:例如从1997年1月1日0时0分0秒到你现在看这篇文章的时间的每一个毫秒数——就都能满足要求(它们都不同,数量也足够,而且还能继续扩展)。

正因如此,“伪随机数”在绝大多数开发环境下都完全能够发挥出类似于“真随机数”的效果(那么到底能不能用计算机生成“真随机数”呢?其实也是可以的,不过这不是本文要讨论的内容)。

道理现在大家都懂了,接下来我们就用一些喜闻乐见的例子来展示如何在Java中使用随机数。

在Java中,获取随机数的方法非常简单。Java本身提供了两个“开箱即用”的工具:一个是专门用于生成随机数的工具类:java.util.Random;另一个则是Math类提供的用于生成随机数的方法:Math.random()。

我们先从第一个,也是最容易理解的说起:Random类。

需要使用Random类,首先需要将其实例化。该类提供了两种实例化方法:Random()和Random(long seed)

示例如下:

JAVA随机数快速入门,java培训

第一种构造器是最贴心的——它会自动找一个“尽可能与同一个程序中其它使用该构造方法生成的Random对象所使用的种子不同的种子”来构造一个新对象。嗯……听起来可能比较拗口,简单地说就是它会尽可能保证该对象提供的随机数是完全“随机”的。

第二种构造器则允许开发者使用指定的种子来生成随机数,其类型是long(它存在的意义是:如果某一天您发明了一种史无前例完美的种子生成算法,能确保生成“真随机数”,那么这种构造器就能派上用场)。

使用Random对象来生成一个随机整形:

JAVA随机数快速入门,java培训

第一个方法是生成一个随机整形,那么它能够提供2^23种可能性。

第二种则是指定生成“0-给定范围”的随机数。例如上图的示例中,其返回值将是0-99中的某一个数值。

很多情况下,上面提到的两个方法足以应对绝大多数随机数生成需求。不过,Random还提供了更多的随机数生成形式:

JAVA随机数快速入门,java培训

上图的示例中,从上至下依次用于:

1.生成一个随机整形(前文已经提到了)

2.生成一个随机双精度浮点型

3.生成一个随机布尔型

4.生成一个随机浮点型

5.生成一个随机长整形

6.使用随机生成的byte结果填满给定的byte数组

可以看出,都十分简单。

编程是一门实践的艺术。现在,我们就尝试使用上述技术做一个很简单并且有趣的游戏:在我们的Java世界中有一个“战士”角色,它(暂定为“它”)每次攻击都会打出一个随机的伤害数值(这就像你玩过的大多数游戏那样)。当然,我们的“战士”是训练有素的,因此其伤害值一定是在100以上,不会比这个值还低。但是这个“战士”的力量也不是无限大,所以它造成的伤害数值最大只能达到200。也就是说,每一次攻击,这位战士根据发挥情况的不同会造成100-200之间的一个随机伤害。

那么,该如何使用上面提到的随机数技术来模拟一下该“战士”攻击5次的效果呢?

首先,我们将“战士类”创造出来:

JAVA随机数快速入门,java培训

可以看出,上图设计的“战士”可以执行“攻击”方法。不过现在这个方法只能返回0——这可不符合前文提到的要求。因此,我们需要赋予它一定范围内的“力量”:

JAVA随机数快速入门,java培训

因为最低伤害值也要到100,因此我们就用100来做返回值的“基底”,在此基础上,浮动范围也是100,因此我们加上一个0-101(不包括)之间的随机数作为最终结果。

注意,这里只需使用一个Random实例即可,因为每次使用该实例执行nextInt()方法时均会获得一个新的随机数,这也是不给定种子时的效果。

OK,设计工作完成。是时候让这个“战士”来展示一下自己的实力了:

JAVA随机数快速入门,java培训

上面的代码中,我们创建了一个“战士”实例并循环执行5次攻击动作,效果是否像我们设计的那样呢?

JAVA随机数快速入门,java培训

嗯,完美。虽然这一次这个“战士”可能没吃饱,攻击力偏低了。我们再试一次:

JAVA随机数快速入门,java培训

可以看出,这次它的发挥要比上一次好很多。只要运气好,次次都能打出200的逆天伤害也是有可能的(当然,概率学告诉我们这种情况非常少见)。

那么,如果我们给定一个“种子”,会怎么样呢?例如这一次我们使用“1L”作为种子创建Random。你会发现:此时,这个战士无论进行多少次测试,每5次的伤害数值均完全一致,包括顺序(这里就不再用图片演示了,可以自行体验)。这也就验证了前文提到的“随机数生成算法依赖于种子”的说法。

那么Random说完了,接下来再介绍一下Math工具类提供的random方法:

JAVA随机数快速入门,java培训

可以看出,这一方法也很简单,它是一个静态方法,因此直接使用Math类进行调用即可。每次调用它均会随机地返回一个范围为0-1(不包括)的double型数值(它无法指定种子,因此使用起来更加简单)。

我相信,有些初学者在第一次看到它会觉得很奇怪:为何这个方法要返回一个如此怪异的结果?

实际上,你能通过这个值获得任意范围的随机数。不信?我们接下来继续上面的例子:

现在,我们的“战士”经过一段时间的刻苦努力,终于从1级的新手成长为99级的老手,伤害的可能值也成长为现在的100-999(下限没变,上限提高了一大截)。

那么,我们该如何用Math提供的random方法来实现这一效果呢?

首先,我们依旧是采用100作为返回值的“基底”,那么浮动数值的取值范围就是0-899(包括)之间。可是前文中已经提到了,Math提供的random方法只能返回一个随机的、0-1之间的double数值。这该怎么办?

先举个简单的例子,假如我要生成5-7(不包括)之间的随机数(也就是只有5,6两种取值),那么可以这样获取:

返回值=5+(int)((7-5)*Math.random())

例如,某一次random返回了0.2,那么7-5=2,2*0.2=0.4,转换为int类型后变为0,最终返回值5+0=5。嗯,这个值确实是5-7之间的一个随机数。

再例如,某一次random反悔了0.8,那么7-5=2,2*0.8=1.6,转换为int后还是1,此时返回值5+1=6,依旧满足条件。

综上,我们可以看出,使用0-1之间的小数产生M到N之间的随机数,可以根据以下公式获得:

结果=M+(int)((M-N)*Math.random())

这样一来,我们的战士也就可以整装待发了(之所以乘以900不是乘以899,是因为产生的随机数无限接近但不包括最大值,因此我们要加上1):

JAVA随机数快速入门,java培训

如上。现在,我们再让它来展示一下自己的实力:

JAVA随机数快速入门,java培训

效果如何呢?如下所示:

JAVA随机数快速入门,java培训

当然,和之前一样,只要运气足够好,终有一天它能打出一刀999的伤害。

在此基础上,还能添加暴击效果,使其有一定几率将伤害数值乘以2,或者添加miss效果,使其有一定几率伤害为0……由此可见,按照这个原理继续扩展的话,终有一天你也能写出一套“只需体验3分钟”就能让用户爱上的款游戏作品了!

当然,那个目标可能并不容易实现。但是至少现在你应该已经掌握了在Java中方使用随机数的方法了,这也是本文的意义所在!

感谢阅读。


发表评论

全国咨询电话:400-611-6270,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(3337544669), 徐老师QQ(1462495461), 刘老师 QQ(3108687497)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2018 华清远见教育集团 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号