help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Help-bash] $RANDOM excludes value zero when called in a subscript


From: Greg Wooledge
Subject: Re: [Help-bash] $RANDOM excludes value zero when called in a subscript
Date: Tue, 6 Aug 2019 16:52:33 -0400
User-agent: Mutt/1.10.1 (2018-07-13)

On Tue, Aug 06, 2019 at 10:07:12PM +0200, Roger Price wrote:
> Your terminology makes me suspect that $RANDOM is of the form
> 
>  X_{n+1} = (a X_n + b) mod m
> 
> with X_0 based on (tv.tv_sec ^ tv.tv_usec ^ getpid ()) or whatever value is
> assigned to RANDOM.

Here's some of the actual code:

===========================================================================
/* Returns a pseudo-random number between 0 and 32767. */
static int
brand ()
{
  /* 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 (2**31 - 1).

     We split up the calculations to avoid overflow.

     h = rseed / 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;


  /* Can't seed with 0. */
  if (rseed == 0)
    rseed = 123459876;
  h = rseed / 127773;
  l = rseed - (127773 * h);
  t = 16807 * l - 2836 * h;
  rseed = (t < 0) ? t + 0x7fffffff : t;

  return ((unsigned int)(rseed & BASH_RAND_MAX));       /* was % 
BASH_RAND_MAX+1 */
}

/* Set the random number generator seed to SEED. */
static void
sbrand (seed)
     unsigned long seed;
{
  rseed = seed;
  last_random_value = 0;
}

static void
seedrand ()
{
  struct timeval tv;
  SHELL_VAR *v;

  gettimeofday (&tv, NULL);
#if 0
  v = find_variable ("BASH_VERSION");
  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid () ^ ((u_bits32_t)&v & 0x7fffffff));
#else
  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
#endif
}

[[ SNIP ]]

int
get_random_number ()
{
  int rv, pid;

  /* Reset for command and process substitution. */
  pid = getpid ();
  if (subshell_environment && seeded_subshell != pid)
    {
      seedrand ();
      seeded_subshell = pid;
    }

  do
    rv = brand ();
  while (rv == last_random_value);
  return rv;
}

static SHELL_VAR *
get_random (var)
     SHELL_VAR *var;
{
  int rv;
  char *p;

  rv = get_random_number ();
  last_random_value = rv;
  p = itos (rv);

  FREE (value_cell (var));

  VSETATTR (var, att_integer);
  var_setvalue (var, p);
  return (var);
}
===========================================================================

> I reduced my test script to one single use of $RANDOM and still saw the same
> anomaly, so it's not caused by multiple instances.

I'm not getting through.

You keep running a NEW instance of bash, a NEW script, what you call a
"sub-script".  This restarts a brand new instance of the bash PRNG.

You are always getting the FIRST result from this newborn PRNG, never
a second, third, tenth, one-thousandth result.

Only the first result.

The "single use of $RANDOM" is precisely the PROBLEM.

You want the opposite of a "single use of $RANDOM".  You want to call
$RANDOM a whole crapload of times in a single process.


> I read your message carefully, several times, but I see no explanation for
> why $RANDOM does not issue zero values from a sub-script.

The first few values from a newborn PRNG are not very random.  I said
this last time.

Do you see the last_random_value variable in the code above?  It's set
to 0 when the PRNG is seeded.  And in get_random_number() rv is rejected
and re-rolled as long as it's equal to last_random_value.

You can't get 0 as the FIRST result from the bash PRNG.  Because the
last_random_value variable is set to 0 at that point, and 0 is therefore
rejected.

You're only ever getting the FIRST result because you keep respawning
a brand new PRNG for every. single. random. value. you. get.

The first few values from a PRNG are not as good as later values.
You want ONE instance of the PRNG to keep churning out numbers for
a while.

If you run a "sub-script" (or just a "script", because that's what it
really is) and only get ONE value from $RANDOM, it won't be very random.
That's the nature of the PRNG.

That's why I gave you my three rules last time.

Redesign your project in such a way that it gets ALL of its random
numbers from a single PRNG.  Not a brand new "sub-script" every time
you want another random number.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]