sci9 Asked: 2018-02-17 19:59:07 +0800 CST2018-02-17 19:59:07 +0800 CST 2018-02-17 19:59:07 +0800 CST Bash:如何使用 $RANDOM 生成随机浮点数 772 是否可以使用整数随机生成器 $RANDOM 生成具有特定精度和特定范围的实随机数?例如,我们如何生成 0 到 1 之间的 4 精度实数? 0.1234 0.0309 0.9001 0.0000 1.0000 一个简单的解决方法: printf "%d04.%d04\n" $RANDOM $RANDOM bash shell 6 个回答 Voted Best Answer Kusalananda 2018-02-18T02:51:11+08:002018-02-18T02:51:11+08:00 awk -v n=10 -v seed="$RANDOM" 'BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }' 这将n在 [0,1) 范围内输出四位小数的随机数(示例中为 10)。它使用rand()in 函数awk(不是标准awk的,但由最常见的awk实现实现)返回该范围内的随机值。随机数生成器由 shell 的$RANDOM变量播种。 当一个awk程序只有BEGIN块(并且没有其他代码块)时,awk不会尝试从其标准输入流中读取输入。 在任何 OpenBSD 系统(或具有相同jot实用程序的系统,最初在 4.2BSD 中)上,以下将生成指定的 10 个随机数: jot -p 4 -r 10 0 1 A.Ellett 2018-08-04T22:49:21+08:002018-08-04T22:49:21+08:00 正如另一个答案中所指出的,您可以使用其他实用程序来生成随机数。在这个答案中,我将资源限制为$RANDOM和一些基本的算术函数。 对于浮点数,尝试类似 printf '%s\n' $(echo "scale=8; $RANDOM/32768" | bc ) 这将为您提供最佳精度,因为$RANDOM仅生成 0 到 32767 之间的数字。(包括 32767!)但是,我还通过调用bc. 但在继续之前,我想看看浮点数的精度和范围这两个问题。之后,我将研究如何生成整数范围(如果您可以生成整数,您可以稍后将它们除以得到小数,如果您希望使用您喜欢的任何实用程序来完成此操作。) 精确 采用 的方法$RANDOM/32768,由于$RANDOM生成从 0 到 32767 的值,因此 的结果$RANDOM/32768同样将是有限多个值。换句话说,它仍然是一个离散的随机变量(使用计算机,你永远无法摆脱这个事实)。考虑到这一点,您可以使用printf. 如果您想要更精细地覆盖区间,您可以开始考虑以 32768 为基数。因此,理论上$RANDOM + $RANDOM*32768应该为您提供 0 到 1,073,741,823 之间的均匀分布。但是,我怀疑命令行能否很好地处理这种精度。与此特定案例有关的几点: 两个独立的、均匀分布的随机变量之和,通常不均匀。在这种情况下,至少从理论上讲(见第三点),它们是。 不要以为你可以简化$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )。这两次发生$RANDOM实际上是两个不同的事件。 我不太了解如何$RANDOM生成以知道像这样调用它两次是否会真正生成两个独立的随机事件。 范围 让我们考虑一下$RANDOM/32768。如果你想要一个范围内的数字,比如说[a,b),那么 $RANDOM/32768*(b-a) + a 将使您进入所需的范围。 整数值的生成 [0,b)首先,考虑在b小于之间生成随机数32768。考虑乘积q*b,其中q是 的整数部分32768/b。那么你可以做的是生成0到32767之间的随机数,但丢弃那些大于或等于的随机数q*b。呼叫由此产生的号码G。则G落在 0 到 的范围内,q*b分布均匀。现在,应用模运算将该值缩减到所需范围: G % b 注意,随机生成一个数字如下 $RANDOM % b 不会创建均匀分布,除非b恰好是 的除数之一32768。 为此编写一个 bash 脚本 q*b如上所述的计算听起来很痛苦。但事实并非如此。您可以按以下方式获取它: q*b = 32768 - ( 32768 % b ) 在 Bash 中,您可以使用 $((32768 - $((32768 % b)) )) 以下代码将生成范围内的随机数0..b(不包括b)。 b=$1 m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n" 附录 从技术上讲,几乎没有理由与之合作 m=$((32768 - $((32768 % $1)) )) 以下将完成同样的事情 a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n" 这是更多的工作,但计算机速度很快。 生成更大范围内的整数 我会让你弄清楚这一点。需要小心,并且在某些时候您必须考虑计算机在处理算术运算时的内存限制。 最后说明 接受的答案不会创建一个 0 到 1 的随机数。 要查看此内容,请尝试以下操作 $ for i in {1..1000}; do echo .$RANDOM; done | awk '{ a += $1 } END { print a }' 对于真正均匀的分布,[0,1)您应该看到接近0.500. 但正如您通过运行上面的代码片段所看到的那样,您将得到类似314.432or的东西322.619。由于是 1000 个数字,因此平均值为.322. 这个生成数字序列的真实平均值是.316362 您可以使用 perl 脚本获得这个真实的平均值 perl -e '{ $i=0; $s=0; while ( $i<=32767 ) { $j = sprintf "%.5f", ".$i"; $j =~ s/^0\.//; print "$j\n"; $s += $j; $i++ }; printf "%.5f\n", $s/32767; }' 我在这里添加整数是为了帮助您了解这种使用方法如何.$RANDOM没有做您最可能希望它做的事情。换句话说,想想哪些整数正在生成,哪些整数被完全遗漏了。跳过了相当多的数字;不少是翻倍的。 user232326 2018-08-05T00:47:25+08:002018-08-05T00:47:25+08:00 在 shell 的 printf 能够理解%a格式(bash ksh zsh 等)并因此能够执行内部基本更改(十六进制 -> 十进制)的系统上([0,1)范围从 0.00003 到 0.99997): printf '%.5f\n' "$(printf '0x0.%04xp1' $RANDOM)" 您甚至可以通过组合更多调用来使用更多数字$RANDOM(从 0.000000001 到 0.999999999) printf '%.9f\n' "$(printf '0x0.%08xp2' $(( ($RANDOM<<15) + $RANDOM )))" 内部(外壳)“$RANDOM”算法基于线性反馈移位寄存器(LFSR)。这些不是加密安全伪随机数生成器 (CSPRNG)。更好的选择是使用/dev/urandom设备中的字节。这将需要调用外部八进制或十六进制转储。 $ printf '%.19f\n' "0x0.$(od -N 8 -An -tx1 /dev/urandom | tr -d ' ')" 0.7532810412812978029 $ printf '%.19f\n' "0x0.$(hexdump -n 8 -v -e '"%02x"' /dev/urandom)" 0.9453460825607180595 获得浮点数的一个非常简单(但不统一)的解决方案是: printf '0.%04d\n' $RANDOM 一种使其在范围内统一的方法[0,1)(不包括1): while a=$RANDOM; ((a>29999)); do :; done; printf '0.%04d\n' "$((a%10000))" αғsнιη 2018-02-17T20:05:26+08:002018-02-17T20:05:26+08:00 利用$(( ( RANDOM % N ) + MIN )) N用您要生成的最小数字替换MAX 数字和 MIN。(N因为 MAX 是唯一的,N+1所以要同时包含 MAX,MIN)。 或者,您可以$(shuf -i MIN-MAX -n 1)改用。 来自man shuf: -i, --input-range=LO-HI treat each number LO through HI as an input line -n, --head-count=COUNT output at most COUNT lines 这里-n 1的shuf意思是只生成一个随机数。 这将使用前导零生成0~9999之间的随机数printf(结果,数字1是唯一的)。 printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215 Andrea993 2019-01-03T13:44:56+08:002019-01-03T13:44:56+08:00 在 bash bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000" 1/10000你的随机精度和4数字你的输出精度在哪里 Stéphane Chazelas 2018-08-05T23:51:11+08:002018-08-05T23:51:11+08:00 zsh在其模块中有一个算术函数(标准函数rand48()的包装器):erand48()zsh/mathfunc zmodload zsh/mathfunc printf '%.4f\n' $((rand48())) 虽然$RANDOM是 15 位、伪随机且可重现,bash但 5.1+ 具有更安全的 32 位整数$SRANDOM,基于可用的真正随机源。它不支持浮点运算,但至少您可以使用它来播种awk的伪随机生成器(否则默认情况下使用非常可预测的结果time()): echo "$SRANDOM" | awk ' { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }' (请记住,它仍然只有 32 位熵,并且awk基于该种子进行确定性伪随机生成)
这将
n
在 [0,1) 范围内输出四位小数的随机数(示例中为 10)。它使用rand()
in 函数awk
(不是标准awk
的,但由最常见的awk
实现实现)返回该范围内的随机值。随机数生成器由 shell 的$RANDOM
变量播种。当一个
awk
程序只有BEGIN
块(并且没有其他代码块)时,awk
不会尝试从其标准输入流中读取输入。在任何 OpenBSD 系统(或具有相同
jot
实用程序的系统,最初在 4.2BSD 中)上,以下将生成指定的 10 个随机数:正如另一个答案中所指出的,您可以使用其他实用程序来生成随机数。在这个答案中,我将资源限制为
$RANDOM
和一些基本的算术函数。对于浮点数,尝试类似
这将为您提供最佳精度,因为
$RANDOM
仅生成 0 到 32767 之间的数字。(包括 32767!)但是,我还通过调用bc
.但在继续之前,我想看看浮点数的精度和范围这两个问题。之后,我将研究如何生成整数范围(如果您可以生成整数,您可以稍后将它们除以得到小数,如果您希望使用您喜欢的任何实用程序来完成此操作。)
精确
采用 的方法
$RANDOM/32768
,由于$RANDOM
生成从 0 到 32767 的值,因此 的结果$RANDOM/32768
同样将是有限多个值。换句话说,它仍然是一个离散的随机变量(使用计算机,你永远无法摆脱这个事实)。考虑到这一点,您可以使用printf
.如果您想要更精细地覆盖区间,您可以开始考虑以 32768 为基数。因此,理论上
$RANDOM + $RANDOM*32768
应该为您提供 0 到 1,073,741,823 之间的均匀分布。但是,我怀疑命令行能否很好地处理这种精度。与此特定案例有关的几点:$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
。这两次发生$RANDOM
实际上是两个不同的事件。$RANDOM
生成以知道像这样调用它两次是否会真正生成两个独立的随机事件。范围
让我们考虑一下
$RANDOM/32768
。如果你想要一个范围内的数字,比如说[a,b)
,那么将使您进入所需的范围。
整数值的生成
[0,b)
首先,考虑在b
小于之间生成随机数32768
。考虑乘积q*b
,其中q
是 的整数部分32768/b
。那么你可以做的是生成0到32767之间的随机数,但丢弃那些大于或等于的随机数q*b
。呼叫由此产生的号码G
。则G
落在 0 到 的范围内,q*b
分布均匀。现在,应用模运算将该值缩减到所需范围:注意,随机生成一个数字如下
不会创建均匀分布,除非
b
恰好是 的除数之一32768
。为此编写一个 bash 脚本
q*b
如上所述的计算听起来很痛苦。但事实并非如此。您可以按以下方式获取它:在 Bash 中,您可以使用
以下代码将生成范围内的随机数
0..b
(不包括b
)。b=$1
附录
从技术上讲,几乎没有理由与之合作
以下将完成同样的事情
这是更多的工作,但计算机速度很快。
生成更大范围内的整数
我会让你弄清楚这一点。需要小心,并且在某些时候您必须考虑计算机在处理算术运算时的内存限制。
最后说明
接受的答案不会创建一个 0 到 1 的随机数。
要查看此内容,请尝试以下操作
对于真正均匀的分布,
[0,1)
您应该看到接近0.500
.但正如您通过运行上面的代码片段所看到的那样,您将得到类似
314.432
or的东西322.619
。由于是 1000 个数字,因此平均值为.322
. 这个生成数字序列的真实平均值是.316362
您可以使用 perl 脚本获得这个真实的平均值
我在这里添加整数是为了帮助您了解这种使用方法如何
.$RANDOM
没有做您最可能希望它做的事情。换句话说,想想哪些整数正在生成,哪些整数被完全遗漏了。跳过了相当多的数字;不少是翻倍的。在 shell 的 printf 能够理解
%a
格式(bash ksh zsh 等)并因此能够执行内部基本更改(十六进制 -> 十进制)的系统上([0,1)
范围从 0.00003 到 0.99997):您甚至可以通过组合更多调用来使用更多数字
$RANDOM
(从 0.000000001 到 0.999999999)内部(外壳)“$RANDOM”算法基于线性反馈移位寄存器(LFSR)。这些不是加密安全伪随机数生成器 (CSPRNG)。更好的选择是使用
/dev/urandom
设备中的字节。这将需要调用外部八进制或十六进制转储。获得浮点数的一个非常简单(但不统一)的解决方案是:
一种使其在范围内统一的方法
[0,1)
(不包括1):利用
$(( ( RANDOM % N ) + MIN ))
N
用您要生成的最小数字替换MAX 数字和 MIN。(N
因为 MAX 是唯一的,N+1
所以要同时包含 MAX,MIN)。或者,您可以
$(shuf -i MIN-MAX -n 1)
改用。来自
man shuf
:这里
-n 1
的shuf
意思是只生成一个随机数。这将使用前导零生成0~9999之间的随机数
printf
(结果,数字1是唯一的)。在 bash
1/10000
你的随机精度和4
数字你的输出精度在哪里zsh
在其模块中有一个算术函数(标准函数rand48()
的包装器):erand48()
zsh/mathfunc
虽然
$RANDOM
是 15 位、伪随机且可重现,bash
但 5.1+ 具有更安全的 32 位整数$SRANDOM
,基于可用的真正随机源。它不支持浮点运算,但至少您可以使用它来播种awk
的伪随机生成器(否则默认情况下使用非常可预测的结果time()
):(请记住,它仍然只有 32 位熵,并且
awk
基于该种子进行确定性伪随机生成)