The class RandomZZStream
is for representing generators of (independent)
uniformly distributed random integers in a given range; the range is
specified when creating the generator (and cannot later be changed). See also
RandomLongStream
for information about generating random machine integers,
and RandomBoolStream
for information about generating random bools.
An alternative way of generating random values is to use a RandomSource
.
There are three ways of creating a new RandomZZStream
object:
RandomZZStream RZS1(lo,hi); // seeded with 1 by default RandomZZStream RZS2(lo,hi, n); // seed generator with abs(n) RandomZZStream RZS3(lo,hi, 0); // seed generator from current time
Each generator will produce values uniformly distributed in the range from lo
to hi
(with both extremes included). An ERR::BadArg
exception is thrown
if lo >= hi
; the case lo == hi
is allowed.
The third argument is for seeding the generator. If you create more than
one RandomZZStream
object with the same seed (and range), they will
each produce exactly the same sequence of values. In particular, to obtain
different results each time a program is run, you can for instance seed the
generator with the system time (e.g. by supplying as argument
time(0)
); this is likely desirable unless you're trying to debug a
randomized algorithm.
Once you have created a RandomZZStream
you may perform the following
operations on it:
*RZS // get the current value of RZS ++RZS // advance to next value of RZS. RZS++ // advance to next value of RZS **INEFFICIENTLY**. sample(RZS) // advance RZS and then return new value; same as ``*++RZS`` out << RZS // print some information about RZS. RZS.myIndex() // number of times RZS has been advanced, same as the number of random values generated.
Note that a RandomZZStream
supports input iterator syntax.
You may assign or create copies of RandomZZStream
objects; the copies
acquire the complete state of the original, so will go on to produce exactly
the same sequence of bits as the original will produce.
The idea is very simple: use the pseudo random number generator of GMP to
generate a random large integer in the range 0 to myRange-1
(where
myRange
was set in the ctor to be 1+myUpb-myLwb
) and then add that
to myLwb
. The result is stored in the data member myValue
so that
input iterator syntax can be supported.
There are two "non essential" data members: mySeed
and myCounter
.
I put these in to help any poor blighter who has to debug a randomized
algorithm, and who may want to "fast forward" the RandomZZSteam
to
the right place.
The data member myState
holds all the state information used by the GMP
generator. Its presence makes the ctors, dtor and assignment messier than
they would have been otherwise.
The advancing and reading member functions (i.e. operator++
and operator*
)
are inline for efficiency, as is the sample
function.
The data members myLwb
, myUpb
and myRange
are morally constant,
but I cannot make them const
because I wanted to allow assignment of
RandomZZStream
objects.
It might be neater to put ++myCounter
inside myGenValue
, though
this would mean that myCounter
gets incremented inside the ctor.
Should sample
advance before or after getting the value?
Is the information printed by myOutputSelf
adequate? Time will tell.
Is there a better way of writing the four ctors without repeating many lines of essentially identical source code?
Discarded idea: have a ctor for RZS which take a ref to a RandomSource
,
and which uses that to obtain randomness. I discarded the idea because of
the risks of an "invisible external reference" (e.g. a dangling reference,
or problems in multithreaded code). Instead of passing a reference to a
RandomSource to the ctor, you can use the RandomSource to create an initial
seed which is handed to the ctor -- this gives better separation.