Unixシェルで擬似乱数を生成するTips
- シェル変数 RANDOM を使う
- シェル変数 SRAND を使う
- デバイスファイル /dev/urandom を使う
- xorshiftを実装する
RANDOM シェル変数を使う
RANDOM を参照するたびに、その値は0
から32767
(0x7fff
) の整数に無作為に展開される。
echo $RANDOM
BashでのRANDOM
https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-RANDOM
RANDOM
Each time this parameter is referenced, it expands to a random integer between 0 and 32767. Assigning a value to this variable seeds the random number generator. If
RANDOM
is unset, it loses its special properties, even if it is subsequently reset.
ZshでのRANDOM
https://zsh.sourceforge.io/Doc/Release/Parameters.html#Parameters
RANDOM
<S>A pseudo-random integer from 0 to 32767, newly generated each time this parameter is referenced. The random number generator can be seeded by assigning a numeric value to
RANDOM
.The values of
RANDOM
form an intentionally-repeatable pseudo-random sequence; subshells that referenceRANDOM
will result in identical pseudo-random values unless the value ofRANDOM
is referenced or seeded in the parent shell in between subshell invocations.
利点
- かんたん、わかりやすい
- Bash、Zshで広く使える
- 乱数の種を仕込むことができる
欠点
- 品質が悪い(線形合同法?のため)
Bashの実装
ソースコード
/* Returns a 32-bit pseudo-random number. */
static u_bits32_t
intrand32 (last)
u_bits32_t last;
{
/* Minimal Standard generator from
"Random number generators: good ones are hard to find",
Park and Miller, Communications of the ACM, vol. 31, no. 10,
October 1988, p. 1195. Filtered through FreeBSD.
x(n+1) = 16807 * x(n) mod (m).
We split up the calculations to avoid overflow.
h = last / q; l = x - h * q; t = a * l - h * r
m = 2147483647, a = 16807, q = 127773, r = 2836
There are lots of other combinations of constants to use; look at
https://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html#Other-random-number-generators */
bits32_t h, l, t;
u_bits32_t ret;
/* Can't seed with 0. */
ret = (last == 0) ? 123459876 : last;
h = ret / 127773;
l = ret - (127773 * h);
t = 16807 * l - 2836 * h;
ret = (t < 0) ? t + 0x7fffffff : t;
return (ret);
}
Zshの実装
Cの標準ライブラリの rand()
関数を素直に呼び出しているよう。
/* Function to get value for special parameter `RANDOM' */
/**/
zlong
randomgetfn(UNUSED(Param pm))
{
return rand() & 0x7fff;
}
シェル変数 SRANDOM を使う
シェル変数 SRANDOM を参照するたびに、32ビット長の整数に無作為に展開される。
bash-5.1で導入された。
echo $SRANDOM
https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-SRANDOM
SRANDOM
This variable expands to a 32-bit pseudo-random number each time it is referenced. The random number generator is not linear on systems that support
/dev/urandom
orarc4random
, so each returned number has no relationship to the numbers preceding it. The random number generator cannot be seeded, so assignments to this variable have no effect. IfSRANDOM
is unset, it loses its special properties, even if it is subsequently reset.
利点
- 従前の RANDOM シェル変数に比べ乱数の品質が良い
欠点
- Bashのバージョンに縛りがある: 導入されたバージョン5.1のリリースは2020年12月
- Zshでは使えない: Zshには URANDOM シェル変数は実装されていない。
- 乱数の種は仕込めない。
当時のNEWS
NEWSThis is a terse description of the new features added to bash-5.1 since the release of bash-5.0. As always, the manual page (doc/bash.1) is the place to look for complete descriptions. [中略] i. SRANDOM: a new variable that expands to a 32-bit random number that is not produced by an LCRNG, and uses getrandom/getentropy, falling back to /dev/urandom or arc4random if available. There is a fallback generator if none of these are available.
Bashの実装
ソースコード
#if !defined (HAVE_GETRANDOM)
/* Imperfect emulation of getrandom(2). */
#ifndef GRND_NONBLOCK
# define GRND_NONBLOCK 1
# define GRND_RANDOM 2
#endif
static ssize_t
getrandom (buf, len, flags)
void *buf;
size_t len;
unsigned int flags;
{
int oflags;
ssize_t r;
static int urand_unavail = 0;
#if HAVE_GETENTROPY
r = getentropy (buf, len);
return (r == 0) ? len : -1;
#endif
if (urandfd == -1 && urand_unavail == 0)
{
oflags = O_RDONLY;
if (flags & GRND_NONBLOCK)
oflags |= O_NONBLOCK;
urandfd = open ("/dev/urandom", oflags, 0);
if (urandfd >= 0)
SET_CLOSE_ON_EXEC (urandfd);
else
{
urand_unavail = 1;
return -1;
}
}
if (urandfd >= 0 && (r = read (urandfd, buf, len)) == len)
return (r);
return -1;
}
#endif
デバイスファイル /dev/urandom を使う
コマンドを実行するたびに、指定した型で乱数が生成される。型は、シングルクォートで囲った部分の引数を適切に与えることで指定できる。
od -An -N'4' -t'u4' /dev/urandom # unsigned 32-bit integer
od -An -N'1' -t'd1' /dev/urandom # signed 8-bit integer
od -An -N'8' -t'f8' /dev/urandom # double-precision floating-point number