   Subject: Random thoughts on HP48 random functions
      Date: 28 May 1997 08:32:17 GMT
      From: jhmeyers@miu.edu (John H Meyers)
Newsgroups: comp.sys.hp48

Everything you never wanted to know about the HP48 Random Number generator,
and were afraid to even ask:

On 1996/07/16, Steve VanDevender posted the following description
of the HP48 RAND function, as implemented by SysRPL function
%RAN at address #2AFC2h:

: Take the 15-digit seed S (0 <= S < 10^15) stored in memory.
: If S == 0, store 999500333083533 in S.  To generate the next
: pseudorandom number, multiply S by 2851130928467 and take the result
: modulo 10^15 (yes, there is no additive constant involved).
: Store that result back in S.  Then divide the result by 10^15 and
: take the 12 most significant digits (truncating rather than rounding
: if there are more than 12 significant digits), and return that
: floating-point number as the next pseudorandom number. [End of quote]

The 15-digit integer random number seed is located at address RANDOMSEED
(#706A4h in the S/SX, #80822h in the G/GX), and may be viewed with
a memory scanner, such as SC from HACK.LIB or "Mon" from DONNELY.GX
(the latter is a utility library actually written by Detlef Mueller,
found at <ftp://hpcvbbs.cv.hp.com/dist/hp48g/utils/misc>).

The RDZ command sets this seed, and each subsequent RAND command then
replaces it with the new 15-digit random integer, as described above;
the details below will serve to illustrate what values are meaningful
for use as arguments to RDZ:

RDZ uses its (real number) argument (X) to set the 15-digit integer
seed; the general idea is that the significant digits of the argument
(but sometimes not all of them, perhaps even none) are used to set the
first twelve digits of the random seed; the exponent of the argument (X)
is sometimes used to set the next two digits, and the last digit
of the random integer seed is always initialized to 1.

More precisely:

o The sign of X is ignored.

o If X is 1 or greater, all twelve mantissa digits of X are used; three
  digits are appended, which are equal to '((XPON(X)+1) MOD 100)*10+1'
  E.g 3.14159265359 --> 314159265359011
                        ^^^^^^^^^^^^    (from mantissa of X)
                                    ^^  (from exponent of X)
                                      ^ (final digit always set to 1)

  It is thus possible to set any one of 9E13 different starting seeds
  using arguments of 1 or greater (the first digit of these can not be
  zero, because all mantissas begin with a non-zero digit); it will
  later be seen that the period of the random number generator
  (before it exactly repeats) is at most 5E13, so there must be
  more than one completely distinct sequence which can be produced
  (sixteen are theoretically possible, but only two are ever generated).

o If X is less than 1 but not less than 1E-12,
  the random integer seed is set to 'IP(1E12*X)*1000+1'
  E.g.
  3.14159265359E-1  --> 314159265359001 (exponent of X is not used)
  3.14159265359E-7  --> 000000314159001
  3.14159265359E-12 --> 000000000003001

  Note that the trailing digits get truncated as X gets smaller; this
  is reminiscent of the HP15C, where all ten fixed digits of the current
  random number (i.e. ten fixed digits following the decimal point)
  could be saved and later restored to resume the sequence, but since
  the HP48 usually truncates the last digits of the 15-digit current seed
  when it returns a real number as a result (and since the last digits
  are usually not 001 after some multiplications have occurred from
  using RAND), it is not possible to do the same on the HP48.

  You could, however, write your own ML function to recall the current
  seed, with 15 digits packed into either a Long Real or Hex string
  object, and likewise another ML function to restore the seed.

o If X is less than 1E-12, all the mantissa digits of X are completely
  ignored, and only the exponent (power of ten) is used to set the seed;
  if XPON(X) is between -13 and -15, the seed is set to 000000000000001;
  otherwise the seed is set to '((XPON(X)+16) MOD 100)*10+1'
  E.g.
  3.14159265359E-13 --> 000000000000001
  3.14159265359E-14 --> 000000000000001
  3.14159265359E-15 --> 000000000000001
  3.14159265359E-16 --> 000000000000001
  3.14159265359E-17 --> 000000000000991
  3.14159265359E-50 --> 000000000000661 etc.

  As you can see, quite a wide range of input values all generate
  the same random seed 000000000000001, and it is somewhat inadvisable
  to ever use any value less than 1E-12 as an argument for RDZ; for
  most purposes, you wouldn't even think of using values less than .1,
  where any trailing significant mantissa digits would be lost.

o If X is zero (requesting a starting value randomized from the clock),
  the least significant 20 bits of the TICKS value are converted
  to an integer, which is then used as above, e.g.
  123456 ticks --> 123456000000061
  234567 ticks --> 234567000000061

  Note that using only 20 bits of TICKS gives us only 2^20 = 1048576
  possible different starting points, which is a lot fewer than we might
  expect, when we could ourselves choose nearly 1E14 different specific
  non-zero starting values for RDZ.  Not only that, but the low-order
  20 bits of TICKS repeats every 2 minutes & 8 seconds, giving rise to
  a distinct possibility of repeating the same sequence.

  Finally, the fact that the significant digits of the seed are all
  at the far left, leaving a generally constant portion at the right,
  makes the first RAND value after 0 RDZ most commonly have the
  same six least significant digits (986636) about 85% of the time,
  which makes it quite apparent that a "more random" seed
  could have been chosen.

  If this is of concern to you, here is an alternative to 0 RDZ,
  which chooses from among 1E12 possible different starting points,
  rather than from only about 1E6, and which can not possibly repeat
  the same starting point again for about 3.87 years:

  \<< RCWS 64 STWS 1E12 TICKS OVER DUP2 / * - B\->R SWAP / RDZ STWS \>>

  This is a slightly improved version of the RDZ0 function which
  I previously posted in a "simple XOR cipher" package, in order
  to provide at least 2^32 different randomized keys per user key,
  so that neither re-using keys nor having common plaintext would
  have the usual bad effects common to such a ciphering method.

  HP could have used TICKS MOD 1E14 had they thought about it,
  which would have made it impossible to repeat in a lifetime;
  hex digits in the seed do not seem to bother RAND at all, so
  it would even seem that the whole 13-nibble TICKS value could
  be pasted in directly, without bothering with the arithmetic!

  BTW, when you need a sequence of random numbers, you should do
  RDZ only once, and thereafter use only RAND; the repeated use of
  0 RDZ (or RDZ0) may paradoxically not produce a distribution
  that passes "randomness" tests as well as the completely
  deterministic sequence generated by RAND; I'm not overly impressed,
  for example, with the slightly skewed results of my old Casio's,
  which I suspect of doing some kind of timing of each keypress
  to generate a "spontaneous" random number (and why only 3 digits?).

Well, so much for RDZ; now, what about RAND?

The HP48 RAND merely multiplies the 15-digit seed by a 13-digit constant
and keeps the low-order 15 digits; what is the period of this sequence,
before it repeats?  Well, the last digit repeats every 4th time, the
last two digits repeat every 20th time, the last three digits repeat
every 100th time, and the last four digits repeat every 500th time.

However, the last five digits repeat only after 5000 RAND's,
the last six digits after 50000, the last seven digits after 5E5,
the last eight digits after 5E6, and the last nine digits after 5E7.

At this rate, the period for 15 digits is at best 5E13;
naturally, this means that some 12-digit truncated real values repeat
during the sequence, even though the internal 15-digit value does not.

Curiously, if you can set the internal seed to 500000000000000, then
every subsequent RAND will return the same value .5; however, you can
not normally arrive at such an internal value using only RDZ and RAND.
You can do it with a memory scanner, however, and then astound your
classmates with a random number generator that outputs a constant :)

This weird behavior could never have happened in the HP15C, where the
internal seed is 10 digits in length, and where it just so happens
that the period of the sequence is also exactly 1E10; you can thus set
any starting seed you want, and every possible seed will produce the same
sequence, just starting at a different point; this is the case because,
unlike the HP48, the HP15C uses a general LCRNG with an additive constant,
and the pair was chosen to yield the maximal period.

Okay, the random rainfall rate is currently down two standard dev's
below the past four hours' moving average, and I'm gettin' outta here
while the chances are maximized :)

-----------------------------------------------------------
With best wishes from:   John H Meyers   <jhmeyers@mum.edu>
