在信息学中,随机数的定义如下:
随机性——不存在统计学偏差,是完全杂乱的数列。
不可预测性——不能通过过去的数列推测出下一个出现的数。
不可重现性——除非将数列本身保存下来,否则不能重现相同的数列。
随机数可能在统计上呈现出某种规律。
在工程上,主要是用到了随机数的两个特性。
1 不可预测性 2 均匀获取数字(在大量随机统计时,每个数出现的期望相同)在安全相关场景中,用到的是随机数的不可预测性。例如,生成秘钥、验证码等场景,让黑客不能找到生成的规律。
在抽奖、负载均衡等场景中,在统计随机数时,每个随机数出现的次数的期望都很接近,结果是公平的。
二 随机数的生成方法在计算机领域主要有两种方法:线性同余算法和硬件设备随机数生成器。
1 线性同余算法a 代码
// 构造函数可设置种子 public Random(long seed) { if (getClass() == Random.class) this.seed = new AtomicLong(initialScramble(seed)); else { // subclass might have overriden setSeed this.seed = new AtomicLong(); setSeed(seed); } } // 随机数生成函数 public int nextInt(int bound) { if (bound <= 0) throw new IllegalArgumentException(BadBound); int r = next(31); int m = bound - 1; if ((bound & m) == 0) // i.e., bound is a power of 2 r = (int)((bound * (long)r) >> 31); else { for (int u = r; u - (r = u % bound) + m < 0; u = next(31)) ; } return r; } // Java 中的随机数都是伪随机数 public static void main(String[] args) { Random r = new Random(100); for (int i = 0; i < 10; i++) { System.out.println(r.nextInt(20) + " "); } }
b 测试结果
15 10 14 8 11 6 16 8 3 13
c 参考
https://www.cnblogs.com/greatfish/p/5845924.html
d 说明
Java 的随机数先设置随机种子,在每次需要随机数的时候调用随机数生成函数生成随机数。
线性同余算法的特点是:只要种子相同,即使在两台不同的机器上,也能产生相同的随机序列。利用这个特性,我们可以在应用层做很多事情。
例如:
时间换空间——只要保存一个种子,就能通过计算得到一个随机数序列,可以利用这个随机数序列实现同步数据的功能。
还原 *** 作——在客户端根据随机序列构建地图,然后在服务端校验 *** 作合法性,只需要同步种子,就能实现相同场景。
线性同余算法计算速度快、实现简单,但是是伪随机——知道种子后能够预测随机数序列,而且随机数序列经过一段时间后会循环重复。
所以在安全性较高的场景,不会使用线性同余算法。
2 类 UNIX 系统的 /dev/random/dev/random 是类 UNIX 系统中的一个特殊设备文件,可以用作随机数发生器或伪随机数发生器。Linux 内核允许程序访问来自设备驱动程序或其他来源的背景噪声。
发生器有一个容纳噪声数据的熵池,/dev/random 设备会返回小于环境噪声总数的随机字节。/dev/random 可生成高随机性的公钥或一次性密码本。
若熵池空了,那么对 /dev/random 的读 *** 作成功将被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得 /dev/random 是真正的随机数发生器,提供了最大可能的随机数据熵,建议在需要生成高强度的密钥时使用 /dev/random。
为了解决阻塞问题, UNIX 系统还提供了一个设备 /dev/urandom,即非阻塞版本。它会重复使用熵池中的数据以产生伪随机数,输出的熵可能小于 /dev/random 的熵。它可以作为生成较低强度密码的伪随机数生成器。
安全使用随机数是比较专业的场景,一般都使用特定的库或算法,直接调用即可,不要自己造算法。
三 误用随机数的场景 1 多次设置随机种子为了让每次程序启动时生成的随机数都是不同的,可以利用 srand() 函数设置不同的随机种子。一般设置的种子为当前时间或进程的进程号等,然后调用 rand() 生成随机数。srand()只需要程序开始时调用一次,rand()是每次需要随机数的时候调用一次。
有的错误用户会把 srand() 和 rand() 同时调用,这样就起不到随机的效果,生成的随机数都一样,而且只获取线性同余算法生成的第一个。也谈不上产生的随机数要满足均匀性的要求。
2 通过取模获取对应范围生成的随机数下面代码每次生成随机数的范围是[0,21)。
int random21 = new Random().nextInt(21);
那下面代码是不是生成[0,10) 的随机数呢?
int random10 = new Random().nextInt(21) % 10;
虽然这种方式得到的数字结果是在[0,10) 之间,但这种方式是错的,因为生成的[0,10) 区间的数字不均匀,生成0这个数字的概率要超过其他9个数字的概率。
random21 产生的[0,10)和[10,20)两个区间的数值的概率是相等,这两个区间的数值运算取模 10 后,值对应[0,10)也是等概率的,但多出的[20,21)这个区间,直接取模后只产生0这个结果。
这样改造还不如下面代码来得直接
3 一定要用随机数吗?int random10 = new Random().nextInt(10);
有时候使用随机数的目的是为了让结果更分散,这种场景下需要了解业务的目的,挑选适合的实现方式,让方案更简单。
例如,针对路由选择,不一定非要用随机数,通过使用计数器实现简单的轮询,也能够实现均匀分发。
四 项目中用到随机数的场景 1 提升用户体验a 歌曲随机播放
b 游戏抽奖
十连抽
普通抽奖
c 游戏武器的暴击
2 时间换空间a 游戏作弊校验
b 节约存储成本
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)