随机数在互联网业务中的应用

随机数在互联网业务中的应用,第1张

随机数在互联网业务中的应用 一 随机数的定义

在信息学中,随机数的定义如下:

随机性——不存在统计学偏差,是完全杂乱的数列。

不可预测性——不能通过过去的数列推测出下一个出现的数。

不可重现性——除非将数列本身保存下来,否则不能重现相同的数列。

随机数可能在统计上呈现出某种规律。

在工程上,主要是用到了随机数的两个特性。

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这个结果。

这样改造还不如下面代码来得直接

int random10 = new Random().nextInt(10);

3 一定要用随机数吗?

有时候使用随机数的目的是为了让结果更分散,这种场景下需要了解业务的目的,挑选适合的实现方式,让方案更简单。

例如,针对路由选择,不一定非要用随机数,通过使用计数器实现简单的轮询,也能够实现均匀分发。

四 项目中用到随机数的场景 1 提升用户体验

a 歌曲随机播放

b 游戏抽奖

十连抽

普通抽奖

c 游戏武器的暴击

2 时间换空间

a 游戏作弊校验

b 节约存储成本

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719192.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存