This is only a preview of the April 2021 issue of Practical Electronics. You can view 0 of the 72 pages in the full issue. Articles in this series:
|
Max’s Cool Beans cunning coding tips and tricks
E
very now and then, we wish to add some amount
of randomness into our programs. For example, we might
want to create an electronic version of a dice. In this case,
we need to use some sort of random-number generator (RNG).
In fact, generating a truly random sequence of numbers is very
difficult. An easier alternative is a pseudo-random number
generator (PRNG), also known as a ‘deterministic random
bit generator’ (DRBG), which is an algorithm for generating
a sequence of numbers whose properties approximate the
properties of sequences of truly random numbers.
Aah, the Arduino
And so we turn our attention to the Arduino and its random()
function, which allows us to generate pseudo-random integers
between a minimum and maximum range that we specify. Generally speaking, the creators of the Arduino tried to make things as
simple as possible for beginners. However, for reasons that defy
explanation, they decided to make the minimum value ‘inclusive’
while the ‘maximum’ value is exclusive’. Consider the following statement, for example: int tmpRnd = random(5, 10);
In this case, tmpRnd will end up containing a pseudo-random value out of the set 5, 6, 7, 8, and 9, but not 10, because 5
(the minimum value) is included in the set while 10 (the maximum value) is excluded. If only one value is specified, then
this is assumed to be the maximum value and the minimum is
automatically set to zero, so random(10) produces identical
results to random(0,10).
One nice thing about the Arduino’s random() function is that
it allows us to generate negative numbers, so random(-10,10)
will return a random value between −10 and +9.
What a memory!
As we will see in a moment, PRNGs need to be provided with
an initial ‘seed’ value before they perform their magic. Unless
we overwrite it, the Arduino will use some default seed value
(I have no idea what this value is). Furthermore, the Arduino’s
PRNG would be classed as non-volatile, because – by default –
it will ‘remember’ and employ the same seed value each time
it’s powered up. Consider the following snippet of code, which
generates groups of five pseudo-random numbers between 0
and 99, for example:
void loop()
{
for (int iRnd = 0; iRnd < 5; iRnd++)
{
int tmpRnd = random(0, 100);
Serial.println(tmpRnd);
}
}
When I run this code on my Arduino Uno, I receive the following
sequence of numbers (I’ve broken them up into groups of five for
clarity): 7, 49, 73, 58, 30; 72, 44, 78, 23, 9... Every time I re-run
the program; I’m presented with exactly the same sequence.
Planting a seed
So, what happens if we change the seed value? Well, let’s assume
we keep the same code in our loop() function as before, but we
add the statement randomSeed(10) to our setup() function.
50
Note that the ‘random seed’ moniker is not intended to imply
that this function generates a random seed value; rather that
it will modify the seed value of the Arduino’s PRNG to be the
specified positive integer, which is 10 in this example (a word
to the wise; don’t use a seed value of 0; nuff said).
Now, when I run this modified code on my Arduino Uno, I
receive the following sequence of numbers (which is different
to what we saw before): 70, 43, 1, 92, 65; 26, 40, 98, 48, 67...
And, once again, every time I re-run the program; I’m presented
with exactly the same sequence.
Just for giggles and grins, what do you think would happen if
we remove the randomSeed(10) statement from the setup()
function and insert it into the loop() function as follows:
void loop()
{
randomSeed(10);
for (int iRnd = 0; iRnd < 5; iRnd++)
{
int tmpRnd = random(0, 100);
Serial.println(tmpRnd);
}
}
Basically, we are resetting our PRNG to the same starting point
before we generate each batch of five random numbers. This
results in our generating the same group of five numbers over
and over again: 70, 43, 1, 92, 65; 70, 43, 1, 92, 65...
Even more random
It may be that we are happy with the fact that the same pseudo-random sequence appears every time we run our program.
Scientists and engineers, for example, often require ‘repeatability’ in their experiments. On the other hand, if you are creating
something like a slot machine destined for the hallowed halls
of Las Vegas, having it perform the same sequence each time
it’s powered up would not be considered to be a desirable feature. And, of course, in some cases, like the programs in this
month’s Cool Beans, we really don’t care either way.
So, how could we set about ensuring that our program employs
a different seed value each time it’s run? One way would be to require the user to press a pushbutton switch, and to use the elapsed
time between power being applied to the system and the user
activating the switch as the seed value for the PRNG. Another
alternative would be to use the fact that any unconnected (‘floating’) analogue inputs on the Arduino tend to wander around in
value. Suppose we return to our original snippet of code but we
add the following statement into our setup() function:
randomSeed( analogRead(A0) );
When we perform the analogRead() on input A0, we are going
to receive a somewhat random value, and this is the value we
use to seed the PRNG. In my case, the first time I ran this program, I received the following sequence: 99, 31, 18, 45, 80; 38,
56, 72, 37, 62... The next time I ran the program, I received a completely different sequence: 41, 78, 21, 52, 19; 23, 73, 52, 87, 34...
To be honest, we’ve only scratched the surface of the topic of
randomness here, but at least we now know enough to be dangerous, as explained here: http://bit.ly/pe-apr21-999999
Practical Electronics | April | 2021
|