在 Bash 中使用 $RANDOM 重复数字
Duplicate numbers using $RANDOM in Bash
我在 while 循环中用 bash 写了一个脚本。代码:
number=0
while [ 1500 -gt $number ]
do
var="abcdefghijklmnopqrstuvxyz"
letter1="${var:$(( RANDOM % ${#var} )):1}"
letter2="${var:$(( RANDOM % ${#var} )):1}"
a=$RANDOM
b=$RANDOM
c=$(( $a * $b))
echo "$letter1$letter2 $c" >> a.txt
number=$(( 1 + $number ))
done
但现在我在结果中看到重复的数字:
Result:
ab 15474
at 15474
yh 15474
gd 15474
re 18696
jg 18696
号码重复。
我猜想 $RANDOM
在一段不变的时间后发生变化,我的脚本再次启动 while
循环比 $RANDOM
变化更快。
你能帮我换个随机化方法吗?
我试过你的脚本,对我来说效果很好。
在bash中generate random number有很多方法。一种是使用 /dev/random
特殊设备文件。 /dev/random
使用从设备驱动程序和其他来源收集的噪声来生成随机数据。 od(八进制转储)命令可以提取多个字节并显示它们的十进制等效值。
od -A n -t d -N 1 /dev/urandom
这里,-t d
指定输出格式为有符号十进制; -N 1
表示从 /dev/urandom 读取一个字节。
另一种方法是使用 jot
命令:
jot -r 10 1 1000
这里生成1到1000之间的10个数。-r
指定生成随机数。
@j23 给出了非常合理的操作性回答。通过对您观察到的行为的可能解释,您正在打印的数字不是单个 $RANDOM
值,而是两个这样的值的乘积。伪随机数生成器 (PRNG) 的成对连续输出不一定像您希望的那样独立。例如,Matlab randn
在 2006 年有一个相关性问题 (arXiv:math/0603058)。
您可以将 tr
与 /dev/urandom
一起使用:
tr -dc 'a-z' </dev/urandom | head -c 2; echo
使用 head
函数的 -c
选项设置所需的字符数。
要生成随机数字,请在 tr
命令中使用另一组:
tr -dc '0-9' </dev/urandom | head -c 4; echo
是这样的吗?
#!/bin/bash
export LC_ALL=C
for((i=0; i<1500; ++i)); do
IFS='' read -n 4 -d '' bytes
#
printf -v a %u "'${bytes:2:1}"
a=$((a%255))
printf -v b %u "'${bytes:3:1}"
b=$((b%255))
printf "%s %s\n" "$(
tr '[=10=]0-13-03-7' 'a-za-za-za-za-za-za-za-za-z' <<<"${bytes:0:2}"
)" $((${a#-}*${b#-}))
done</dev/urandom
当字符代码高于 0x80 时,%u
转换会奇怪地创建非常大的数字,并且它的模 255 会创建一个负数,因此我不得不做一些不明显的解决方法来解决这个问题。或许您可以想出一种不那么扭曲的方法,将两个字节解压缩为一个无符号的 15 位数字。
这是一个更新,它在第二列中获取 200000-1000000 范围内的结果。它需要两个额外的随机字节,然后对结果执行模和加法以使其进入正确的范围。这超出了 Bash 内置算法的范围,所以我改用 bc
。
#!/bin/bash
export LC_ALL=C
for((i=0; i<1500; ++i)); do
IFS='' read -n 6 -d '' bytes
#
printf -v a %u "'${bytes:2:1}"
a=$((a%255))
printf -v b %u "'${bytes:3:1}"
b=$((b%255))
printf -v c %u "'${bytes:4:1}"
c=$((c%255))
printf -v d %u "'${bytes:5:1}"
d=$((d%255))
printf "%s %s\n" "$(
tr '[=11=]0-13-03-7' 'a-za-za-za-za-za-za-za-za-z' <<<"${bytes:0:2}"
)" $(bc <<<"((${a#-}*${b#-}*${c#-}*${d#-})%800000)+200000")
done</dev/urandom
不过,这变得相当复杂。如果 Bash 对你来说不是必需的,试试这个 Python 3 脚本。
from random import choice, randrange
from string import ascii_lowercase
for r in range(1500):
print('{0}{1} {2}'.format(
choice(ascii_lowercase),
choice(ascii_lowercase),
200000+randrange(999999800000)))
我在 while 循环中用 bash 写了一个脚本。代码:
number=0
while [ 1500 -gt $number ]
do
var="abcdefghijklmnopqrstuvxyz"
letter1="${var:$(( RANDOM % ${#var} )):1}"
letter2="${var:$(( RANDOM % ${#var} )):1}"
a=$RANDOM
b=$RANDOM
c=$(( $a * $b))
echo "$letter1$letter2 $c" >> a.txt
number=$(( 1 + $number ))
done
但现在我在结果中看到重复的数字:
Result:
ab 15474
at 15474
yh 15474
gd 15474
re 18696
jg 18696
号码重复。
我猜想 $RANDOM
在一段不变的时间后发生变化,我的脚本再次启动 while
循环比 $RANDOM
变化更快。
你能帮我换个随机化方法吗?
我试过你的脚本,对我来说效果很好。
在bash中generate random number有很多方法。一种是使用 /dev/random
特殊设备文件。 /dev/random
使用从设备驱动程序和其他来源收集的噪声来生成随机数据。 od(八进制转储)命令可以提取多个字节并显示它们的十进制等效值。
od -A n -t d -N 1 /dev/urandom
这里,-t d
指定输出格式为有符号十进制; -N 1
表示从 /dev/urandom 读取一个字节。
另一种方法是使用 jot
命令:
jot -r 10 1 1000
这里生成1到1000之间的10个数。-r
指定生成随机数。
@j23 给出了非常合理的操作性回答。通过对您观察到的行为的可能解释,您正在打印的数字不是单个 $RANDOM
值,而是两个这样的值的乘积。伪随机数生成器 (PRNG) 的成对连续输出不一定像您希望的那样独立。例如,Matlab randn
在 2006 年有一个相关性问题 (arXiv:math/0603058)。
您可以将 tr
与 /dev/urandom
一起使用:
tr -dc 'a-z' </dev/urandom | head -c 2; echo
使用 head
函数的 -c
选项设置所需的字符数。
要生成随机数字,请在 tr
命令中使用另一组:
tr -dc '0-9' </dev/urandom | head -c 4; echo
是这样的吗?
#!/bin/bash
export LC_ALL=C
for((i=0; i<1500; ++i)); do
IFS='' read -n 4 -d '' bytes
#
printf -v a %u "'${bytes:2:1}"
a=$((a%255))
printf -v b %u "'${bytes:3:1}"
b=$((b%255))
printf "%s %s\n" "$(
tr '[=10=]0-13-03-7' 'a-za-za-za-za-za-za-za-za-z' <<<"${bytes:0:2}"
)" $((${a#-}*${b#-}))
done</dev/urandom
当字符代码高于 0x80 时,%u
转换会奇怪地创建非常大的数字,并且它的模 255 会创建一个负数,因此我不得不做一些不明显的解决方法来解决这个问题。或许您可以想出一种不那么扭曲的方法,将两个字节解压缩为一个无符号的 15 位数字。
这是一个更新,它在第二列中获取 200000-1000000 范围内的结果。它需要两个额外的随机字节,然后对结果执行模和加法以使其进入正确的范围。这超出了 Bash 内置算法的范围,所以我改用 bc
。
#!/bin/bash
export LC_ALL=C
for((i=0; i<1500; ++i)); do
IFS='' read -n 6 -d '' bytes
#
printf -v a %u "'${bytes:2:1}"
a=$((a%255))
printf -v b %u "'${bytes:3:1}"
b=$((b%255))
printf -v c %u "'${bytes:4:1}"
c=$((c%255))
printf -v d %u "'${bytes:5:1}"
d=$((d%255))
printf "%s %s\n" "$(
tr '[=11=]0-13-03-7' 'a-za-za-za-za-za-za-za-za-z' <<<"${bytes:0:2}"
)" $(bc <<<"((${a#-}*${b#-}*${c#-}*${d#-})%800000)+200000")
done</dev/urandom
不过,这变得相当复杂。如果 Bash 对你来说不是必需的,试试这个 Python 3 脚本。
from random import choice, randrange
from string import ascii_lowercase
for r in range(1500):
print('{0}{1} {2}'.format(
choice(ascii_lowercase),
choice(ascii_lowercase),
200000+randrange(999999800000)))