Contents

在Linux中生成随机数

1. 概述

当我们需要为模拟用例**选择一个无偏的数字时,随机数非常有用。**在本教程中,我们将学习一些在 Linux 环境中生成随机数的常用方法。

2. 伪随机数发生器

根据定义,随机数是不可预测的。另一方面,计算机通过设计产生确定性 输出。因此,它们产生的随机数 )并不是真正的随机,而只是伪随机。

伪随机数生成器 (PRNG) 使用计算算法来生成大量随机结果。然而,实际上,这些序列中的所有值都依赖于一个初始值,我们称之为种子值。如果我们知道种子值和算法,我们就可以精确地确定整个序列,并且所有的值都可以很容易地预测出来。

问题在于,只要种子未知且算法足够复杂,无法猜测,序列就会显得是随机的。因此,在最常见的用例中,使用 PRNG 是安全的。但是,如果用例强烈依赖于随机性,那么我们将切换到使用硬件生成随机数 的物理方法。

在接下来的几节中,我们的重点将限于使用流行的 Unix 实用程序生成伪随机数。

3. Bash中的*$RANDOM*

假设我们要模拟一个掷骰子的事件,该掷骰子的数字从 1 到 6 枚举。

为此,我们可以*使用*$RANDOM,这是一个内置的 Bash 变量,它提供一个伪随机数:

$ echo $RANDOM
30627
$ echo $RANDOM
10419

使用*$RANDOM*,我们得到一个介于0 和32767之间的数字。但是,在这种情况下,我们需要将随机值限制16之间,因此我们可以使用模 (%) 运算符来限制min=1max=6 之间的值:

$ cat dice.sh
#!/bin/bash
function roll_dice {
    min=1
    max=6
    number=$(expr $min + $RANDOM % $max)
    echo $number
}

请注意,我们在这里没有提供种子值,这意味着 Bash 将使用默认的种子值来生成随机序列。如果我们通过同时调用这个函数来生成两个序列,那么我们得到相同序列的机会就很高。

因此,建议我们通过显式初始化来引入种子值的变化:

RANDOM=$$

我们知道这两个调用会有不同的进程 ID,我们可以使用 PID ( $$ ) 作为种子值

最后,让我们通过roll.sh脚本调用这个函数:

$ cat roll.sh
#!/bin/bash
. ./dice.sh
roll_dice
$ ./roll.sh
4
$ ./roll.sh
3
$ ./roll.sh
5

4. 使用awk

awk 是一种用于处理文本文件的有用语言和 Unix 实用程序。让我们看看如何使用awk生成随机数:

4.1. 数据集

假设我们有一个文件students.txt,其中包含属于不同组的学生的姓名:

$ cat students.txt
Bryan,Roger,Christina
Rishabh,Mary,Rose
Paul,Vikram,Yasim
Leo,Immy,Kudrat

现在,我们需要从每组中随机选择一名学生。

4.2. srand()rand()

awk中,我们可以访问两个函数,即srand()rand(),它们可以生成一个介于 0 和 1 之间的随机浮点数。在这种情况下,*srand()*帮助我们初始化种子值以生成随机序列。

让我们在choose.awk文件中编写我们的awk脚本:

#!/bin/awk
BEGIN {
    FS=",";
    srand(seed);
}
{
    field=int(1.0 + rand()*NF)
    print $field
}

本质上,我们正在对BEGIN 块中的字段分隔符 ( FS )和种子进行一次性初始化。稍后,我们**通过将浮点值乘以该行中的字段数 (NF )**将其缩放为整数值。

最后,让我们通过使用*-v*标志将种子值作为外部变量提供来执行我们的脚本:

$ awk -v seed=$RANDOM -f choose.awk students.txt
Christina
Rose
Yasim
Kudrat

当然,我们可以多次执行,看看我们实际上每次都得到随机结果。

5. 使用伪设备文件

在内部,数字存储为位。因此,另一种生成随机数的方法是生成随机字节。在本节中,我们将使用伪设备文件作为 PRNG。

5.1. /dev/random和*/dev/urandom*

在 Unix 中,一切都是文件 ,其中也包括设备,这些设备安装在文件系统的*/dev虚拟目录下。此外,Unix 还有一组特殊的伪设备文件,它们实际上与真实硬件无关。**其中,/dev/random/dev/urandom*是两个用作 PRNG 的伪设备文件**:

$ ls -l /dev/*random
crw-rw-rw- 1 root root 1, 8 Oct 14 13:30 /dev/random
crw-rw-rw- 1 root root 1, 9 Oct 14 13:30 /dev/urandom

正如我们所见,所有用户都可以读取/写入这些文件,但没有用户具有执行 ( x ) 权限。

有趣的是,**Unix 内核从各种设备收集噪声数据并将它们传输到内部 *池。此外,/dev/random/dev/urandom*可以在内部访问这种噪声并产生随机字节的数据。

对所有这些在内部如何工作有了基本的了解,让我们看看它们有何不同。嗯,** urandom中的“ u”代表无限**。因此,它本质上意味着即使熵池包含的熵少于满足请求所需的熵,/dev/urandom也永远不会阻塞。但是,在这种情况下, /dev/random将被阻止。因此,根据随机性和延迟的接受标准,我们应该做出正确的选择。

5.2. 随机字节中的随机数

让我们把我们对这些伪设备的理论理解来生成一个大小为 4 字节的随机整数。为此,我们可以使用dd 命令:

# dd if=/dev/urandom of=~/random_number count=4 bs=1
4+0 records in
4+0 records out
4 bytes copied, 0.0002276 s, 17.6 kB/s

我们实际上是将4字节的数据从*/dev/urandom复制到~/random_number*。由于数据存储在原始字节中,我们不能使用诸如cat 之类的常用命令来读取它。相反,我们必须使用od 命令来读取原始字节以形成一个整数:

$ od -An --format=dI ~/random_number
   449690100

我们必须注意,我们将格式指定为dI(十进制整数),以便od可以将其读取为整数。我们还使用*-An*来抑制输出中的偏移地址字段。