在 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)))