在Linux中生成随机数
1. 概述
当我们需要为模拟用例**选择一个无偏的数字时,随机数非常有用。**在本教程中,我们将学习一些在 Linux 环境中生成随机数的常用方法。
2. 伪随机数发生器
根据定义,随机数是不可预测的。另一方面,计算机通过设计产生确定性 输出。因此,它们产生的随机数 )并不是真正的随机,而只是伪随机。
伪随机数生成器 (PRNG) 使用计算算法来生成大量随机结果。然而,实际上,这些序列中的所有值都依赖于一个初始值,我们称之为种子值。如果我们知道种子值和算法,我们就可以精确地确定整个序列,并且所有的值都可以很容易地预测出来。
问题在于,只要种子未知且算法足够复杂,无法猜测,序列就会显得是随机的。因此,在最常见的用例中,使用 PRNG 是安全的。但是,如果用例强烈依赖于随机性,那么我们将切换到使用硬件生成随机数 的物理方法。
在接下来的几节中,我们的重点将限于使用流行的 Unix 实用程序生成伪随机数。
3. Bash中的*$RANDOM*
假设我们要模拟一个掷骰子的事件,该掷骰子的数字从 1 到 6 枚举。
为此,我们可以*使用*$RANDOM,这是一个内置的 Bash 变量,它提供一个伪随机数:
$ echo $RANDOM
30627
$ echo $RANDOM
10419
使用*$RANDOM*,我们得到一个介于0 和32767之间的数字。但是,在这种情况下,我们需要将随机值限制 在1和6之间,因此我们可以使用模 (%) 运算符来限制min=1和max=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*来抑制输出中的偏移地址字段。