POV-Ray : Newsgroups : povray.binaries.images : Toward a less-lame rand() function. : Re: Toward a less-lame rand() function. Server Time
1 Aug 2024 00:22:17 EDT (-0400)
  Re: Toward a less-lame rand() function.  
From: Jay Fox
Date: 20 May 2009 12:35:00
Message: <web.4a14303b2a89ae6dd92e869d0@news.povray.org>
"clipka" <nomail@nomail> wrote:
> "Kenneth" <kdw### [at] earthlinknet> wrote:
> > If my simplistic assumptions/understandings are correct, then it seems to me
> > that it all still begs the question of why there are repetitive patterns when
> > using seed(frame_number) in animations--albeit when calling only one rand()
> > value per seed. I don't mean to beat this poor old horse to death, but
> > seed(frame number 23) and seed(frame number 24) should--as far as I understand
> > things--pick up the 2^32 stream at wildly differing locations (with completely
> > different 'blocks' of random values that rand() then picks out--even just one
> > rand() call.) If the 2^32 values in the stream are indeed pseudo-randomly
> > created, then from where does a semi-repeating pattern arise?
>
> I'm a bit puzzled about this one as well. I guess I'll have to look at the
> actual code one of these days. It *may* be the case that the first rand()
> actually does not yet pull the value *after* the "23", but the "23" itself -
> somewhat garbled due to the hashing always performed, but still with a
> recognizable correlation to the seed value.
>
> It might be interesting to see how the "single-rand()-per-frame" thing works out
> when you discard the first result of rand(), and use only the second result,
> e.g.:
>
> #declare R = seed(4711);
> #declare dummy = rand(R);
> #declare myValue = rand(R);

A simple way to understand the problem is to use a simple example. An LCG works
by logic like this:
R[0] = seed
R[i] = mod(A * R[i-1] + C, Base)
rand()[i] = R[i]/Base

So rand[i], with i>0, will be the random sequence.

In a typical example, A might be 69069, C might be an odd number like 1234567,
and Base might be 2^32.

A simple example might be A=21, C=3, and Base = 100.

If we start with a seed of 1, then the first few rand's will be 0.24, 0.07,
0.50, 0.53, 0.16, 0.39, 0.22, 0.65, etc. Looks pretty random, and for practical
purposes, where you need fewer than, say a dozen random numbers, it works fine.

However, if we only look at the first random value, using consecutive seeds, we
get the following pattern:

seed(1); rand() = 0.24
seed(2); rand() = 0.45
seed(3); rand() = 0.66
seed(4); rand() = 0.87
seed(5); rand() = 0.08
seed(6); rand() = 0.29

Notice that each is exactly 0.21 larger than the one before (mod 100). A very
regular pattern.

But what if we take the second rand() value for each seed?

seed(1); rand(); rand() = 0.07
seed(2); rand(); rand() = 0.48
seed(3); rand(); rand() = 0.89
seed(4); rand(); rand() = 0.30
seed(5); rand(); rand() = 0.71
seed(6); rand(); rand() = 0.12

Again, there is a regular pattern: each is 0.41 larger than the one before (mod
100).

Sorry folks, but no free lunch. Multiple rand() streams with consecutive seeds
will show a very regular pattern. The pattern continues, regardless of how far
we go into the stream.

As a practical example, if we grabbed the first three rand() values and made
them into vector, and we used the frame number as the seed, then the vector
will follow a well-defined lattice pattern, i.e., it will change by the same
amount from frame to frame, mod 1.0 on each axis to stay in the unit cube.

An LCG is only useful as an RNG if we pick one and only one seed. Therefore,
from frame to frame, we need to seed with the last rand() value from the
previous frame. Unless, of course, we use a different RNG.


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.