Reputation: 10364
I'm trying to generate a random number in between a specified range by using the $RANDOM function in the bash terminal. The problem is that the numbers it is generating don't appear to be random at all. The script I am using is:
RANDOM=$$;
a=$RANDOM
b=9; #Number of scripts in collection -1
c=`expr $a % $b `; #Random number between 0 and b
if (( c==0 ));
then
echo "script 1 (random = $a, mod = $c)";
elif (( c==1 ));
then
echo "stript 2 (random = $a, mod = $c)";
...
else
echo "error (random = $a, mod = $c)";
fi;
If I run this in a for in loop I get, for example:
script 8 (random = 17845, mod = 7)
script 8 (random = 18754, mod = 7)
script 8 (random = 19663, mod = 7)
script 7 (random = 20571, mod = 6)
script 7 (random = 21480, mod = 6)
script 6 (random = 22388, mod = 5)
script 6 (random = 23297, mod = 5)
script 6 (random = 24206, mod = 5)
script 5 (random = 25114, mod = 4)
script 5 (random = 26023, mod = 4)
Which clearly isn't random.
I tried removing the $a and just running
c=`expr $RANDOM % $b`;
and then altering the code to another variation
c=`expr $a \* $b \/ 32767`;
But these (unsurprisingly) returned the same result. What am I doing wrong? Or is this just a slightly irritating limitation for $RANDOM? Any advice would be greatly appreciated.
Upvotes: 1
Views: 3095
Reputation: 2399
I think the explanation is to be found here:
When you use a modulus operation you are selecting information from the low order bits of a number and discarding information from the high order bits... The least significant (right-hand) digits of X are not very random, so decisions based on the number X should always be influenced primarily by the most significant digits.
And using this does work better for me (though I only tested a few times):
c=$(($a * $b / 32768))
Here's the revised script:
#!/bin/bash
RANDOM=$$;
a=$RANDOM
b=9; #Number of scripts in collection -1
c=$(($a * $b / 32768))
if (( c==0 )); then
echo "script 1 (random = $a, mod = $c)";
elif (( c==1 )); then
echo "script 2 (random = $a, mod = $c)";
else
echo "error (random = $a, mod = $c)";
fi;
Hope this helps.
Upvotes: 2
Reputation: 75498
You kept seeding RANDOM with the same number. Try not to seed it or seed it with a more random item instead:
RANDOM=$$
Apparently $$
doesn't change always as it's always the main PID (not subshell PID) of your shell. If you're actually calling different shells, probably there isn't much difference since the numbers seeded by every PID increments only by ones. So either you could remove that or get another random seed somewhere like /dev/urandom
, etc.
One good way to apply a random seed by /dev/urandom
:
RANDOM=$(tr -dc 0-9 < /dev/urandom | head -c10)
Another through nanoseconds (seeding larger numbers than these seems to not give a good effect):
RANDOM=$(date '+%N')
Also to make it look more unique among different subprocesses, add BASHPID (better than $$) to your seed:
RANDOM=$(( BASHPID + $(date '+%N') ))
Upvotes: 3