2

Environment:

  • Debian
  • Bash

What is known:

RANDOM gives a random number in a range of 15 bits by the following:

echo $RANDOM

SRANDOM gives a random number in a range of 32 bits by the following:

echo $SRANDOM

RANDOM can be configured to create random numbers in a 30-bit range by the following:

my_rnd=$(((RANDOM<<15|RANDOM)))
echo "$my_rnd"

RANDOM can be configured to create random numbers in a 45-bit range by the following:

my_rnd=$(((RANDOM<<15|RANDOM)<<15|RANDOM))
echo "$my_rnd"

How can I configure RANDOM to get the same 32-bit range of random numbers as I get when using SRANDOM?

Alfred.37
  • 204
  • 1
  • 5
  • 23
  • In general, if you need anything other than the simplest random numbers, bash is not a good language to write this. Moreover, POSIX only guarantees arithmetic in signed long, so you might get a signed 32-bit integer here and therefore a negative value. Calling Ruby's SecureRandom.rand would be a better option. – bk2204 Jul 21 '21 at 22:30

2 Answers2

6
my_rnd=$(((RANDOM<<15|RANDOM)))

What this does is only to use RANDOM twice, for 15 bits each, with the first value moved by 15 bits to the left. So you get a binary number like aaaaaaaaaaaaaaabbbbbbbbbbbbbbb, where the a's are bits representing the first value and the b's the ones representing the second.

Similarly, to get any number of bits, you could use either RANDOM or SRANDOM to generate at least that many bits, collected together with shift (<<) and or (|), and then mask the rest out with and (&). E.g. for 32 bits, you'd use a mask of 0xffffffff :

echo "$(( ((RANDOM<<30) | (RANDOM<<15) | RANDOM) & 0xffffffff ))"

(0xffffffff is the maximum 32-bit binary value, 4294967295 in decimal. You could also use ((1 << 32) - 1) to calculate it on the fly.)


Note that Bash's manual doesn't seem to make any promises about how non-predictable the values produced by RANDOM are, but for SRANDOM it says that:

SRANDOM
This variable expands to a 32-bit pseudo-random number each time it is referenced. The random number generator is not linear on systems that support /dev/urandom or arc4random, so each returned number has no relationship to the numbers preceding it.

which implies that RANDOM could indeed be a linear congruential generator, which is to say, not a very good random generator. /dev/urandom and arc4random are implemented with better algorithms, so if it's available, you should use SRANDOM.


Even if SRANDOM is not available in your version of Bash, chances are your system has /dev/urandom available. So, if you do need better random numbers than what RANDOM provides, you could use that directly. Building on an answer in Using /dev/random, /dev/urandom to generate random data, this would fill the shell array arr with n random 32-bit numbers:

n=10
arr=( $(od -vAn -N $((n*4)) -tu4 < /dev/urandom) )

(using word splitting on purpose, IFS must not contain digits.)

ilkkachu
  • 138,973
5

You could just do:

(( my_rnd = (RANDOM << 17) | (RANDOM << 2) | (RANDOM & 3) ))

That is, take all 15 bits of a first RANDOM, shifted 17 bits, another 15 bits of another RANDOM, shifted 2 bits, and an extra 2 bits of a third RANDOM (the lower ones, you could also use RANDOM >> 13 for the higher ones, which at least in older versions of bash were significantly more random).

  • Its sounds for me the quality of RANDOM on new and old systems can be verry differnt. Which solution can be a good choice for unknown systems, which can be verry or old r actual also ? – Alfred.37 Jul 21 '21 at 22:18
  • 1
    @Eddy763, the poor randomness in the low bits is something I remember from the 90s. I don't know exactly when it was fixed but it probably was over 10 years ago so you're probably fine nowadays. In any case, whilst (pseudo-)random, $RANDOM is not cryptographically safe in that it is predictable. So not OK to generate passwords for instance. – Stéphane Chazelas Jul 22 '21 at 06:18
  • 1
    @Eddy763 The Wikipedia page on LCGs has a couple of notes about the weaknesses in the low bits, but they depend on the magic numbers of the LCG. Bash looks to use x * 16807 mod 2^31-1, and uses a 32-bit value but returns only 15 bits, so it doesn't have the most critical issues. (Which would come if the modulus was a power of two.) It looks like the same equation since 4.0. But no, not in the same ballpark as actual cryptographically strong RNGs. – ilkkachu Jul 29 '21 at 11:19
  • @Stéphane Chazelas Can you add the solution for the follow "you could also use RANDOM >> 13" THX – Alfred.37 Apr 07 '23 at 14:10