Closed12

Unixシェルで擬似乱数を生成するTips

四ツ山伊吹四ツ山伊吹
  • シェル変数 RANDOM を使う
  • シェル変数 SRAND を使う
  • デバイスファイル /dev/urandom を使う
  • xorshiftを実装する
四ツ山伊吹四ツ山伊吹

RANDOM シェル変数を使う

RANDOM を参照するたびに、その値は0から32767 (0x7fff) の整数に無作為に展開される。

bash/zsh
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 reference RANDOM will result in identical pseudo-random values unless the value of RANDOM is referenced or seeded in the parent shell in between subshell invocations.

四ツ山伊吹四ツ山伊吹

利点

  • かんたん、わかりやすい
  • Bash、Zshで広く使える
  • 乱数の種を仕込むことができる

欠点

  • 品質が悪い(線形合同法?のため)
四ツ山伊吹四ツ山伊吹

Bashの実装

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/random.c?h=bash-5.2#n53

ソースコード
lib/sh/random.c
/* 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);
}
四ツ山伊吹四ツ山伊吹

シェル変数 SRANDOM を使う

シェル変数 SRANDOM を参照するたびに、32ビット長の整数に無作為に展開される。

bash-5.1で導入された。

bash
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 or arc4random, 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. If SRANDOM is unset, it loses its special properties, even if it is subsequently reset.

四ツ山伊吹四ツ山伊吹

利点

  • 従前の RANDOM シェル変数に比べ乱数の品質が良い

欠点

  • Bashのバージョンに縛りがある: 導入されたバージョン5.1のリリースは2020年12月
  • Zshでは使えない: Zshには URANDOM シェル変数は実装されていない。
  • 乱数の種は仕込めない。
四ツ山伊吹四ツ山伊吹

当時のNEWS

NEWS
This 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の実装

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/random.c?h=bash-5.2#n180

ソースコード
lib/sh/random.c
#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 を使う

コマンドを実行するたびに、指定した型で乱数が生成される。型は、シングルクォートで囲った部分の引数を適切に与えることで指定できる。

bash
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
四ツ山伊吹四ツ山伊吹

利点

  • Unixシェルに依らず利用できる
  • 多様な形式の乱数を生成できる

欠点

  • オーバーヘッドが大きめ
    • 外部コマンド(ここでは od)を参照する
    • デバイスファイルを参照する
  • 乱数の種を仕込めない
このスクラップは2023/10/30にクローズされました