 ## –Э–∞—И–Є –њ–∞—А—В–љ–µ—А—Л

–Ъ–љ–Є–≥–Є –њ–Њ Linux (—Б –Њ—В–Ј—Л–≤–∞–Љ–Є —З–Є—В–∞—В–µ–ї–µ–є)

–С–Є–±–ї–Є–Њ—В–µ–Ї–∞ —Б–∞–є—В–∞ rus-linux.net

# 9.6. \$RANDOM: generate random integer

`\$RANDOM` is an internal Bash function (not a constant) that returns a pseudorandom  integer in the range 0 - 32767. It should `not` be used to generate an encryption key.

Example 9-26. Generating random numbers

 ```#!/bin/bash # \$RANDOM returns a different random integer at each invocation. # Nominal range: 0 - 32767 (signed 16-bit integer). MAXCOUNT=10 count=1 echo echo "\$MAXCOUNT random numbers:" echo "-----------------" while [ "\$count" -le \$MAXCOUNT ] # Generate 10 (\$MAXCOUNT) random integers. do number=\$RANDOM echo \$number let "count += 1" # Increment count. done echo "-----------------" # If you need a random int within a certain range, use the 'modulo' operator. # This returns the remainder of a division operation. RANGE=500 echo number=\$RANDOM let "number %= \$RANGE" # ^^ echo "Random number less than \$RANGE --- \$number" echo # If you need a random integer greater than a lower bound, #+ then set up a test to discard all numbers below that. FLOOR=200 number=0 #initialize while [ "\$number" -le \$FLOOR ] do number=\$RANDOM done echo "Random number greater than \$FLOOR --- \$number" echo # Let's examine a simple alternative to the above loop, namely # let "number = \$RANDOM + \$FLOOR" # That would eliminate the while-loop and run faster. # But, there might be a problem with that. What is it? # Combine above two techniques to retrieve random number between two limits. number=0 #initialize while [ "\$number" -le \$FLOOR ] do number=\$RANDOM let "number %= \$RANGE" # Scales \$number down within \$RANGE. done echo "Random number between \$FLOOR and \$RANGE --- \$number" echo # Generate binary choice, that is, "true" or "false" value. BINARY=2 T=1 number=\$RANDOM let "number %= \$BINARY" # Note that let "number >>= 14" gives a better random distribution #+ (right shifts out everything except last binary digit). if [ "\$number" -eq \$T ] then echo "TRUE" else echo "FALSE" fi echo # Generate a toss of the dice. SPOTS=6 # Modulo 6 gives range 0 - 5. # Incrementing by 1 gives desired range of 1 - 6. # Thanks, Paulo Marcel Coelho Aragao, for the simplification. die1=0 die2=0 # Would it be better to just set SPOTS=7 and not add 1? Why or why not? # Tosses each die separately, and so gives correct odds. let "die1 = \$RANDOM % \$SPOTS +1" # Roll first one. let "die2 = \$RANDOM % \$SPOTS +1" # Roll second one. # Which arithmetic operation, above, has greater precedence -- #+ modulo (%) or addition (+)? let "throw = \$die1 + \$die2" echo "Throw of the dice = \$throw" echo exit 0```

Example 9-27. Picking a random card from a deck

 ```#!/bin/bash # pick-card.sh # This is an example of choosing random elements of an array. # Pick a card, any card. Suites="Clubs Diamonds Hearts Spades" Denominations="2 3 4 5 6 7 8 9 10 Jack Queen King Ace" # Note variables spread over multiple lines. suite=(\$Suites) # Read into array variable. denomination=(\$Denominations) num_suites=\${#suite[*]} # Count how many elements. num_denominations=\${#denomination[*]} echo -n "\${denomination[\$((RANDOM%num_denominations))]} of " echo \${suite[\$((RANDOM%num_suites))]} # \$bozo sh pick-cards.sh # Jack of Clubs # Thank you, "jipe," for pointing out this use of \$RANDOM. exit 0```

Example 9-28. Brownian Motion Simulation

 ```#!/bin/bash # brownian.sh # Author: Mendel Cooper # Reldate: 10/26/07 # License: GPL3 # ---------------------------------------------------------------- # This script models Brownian motion: #+ the random wanderings of tiny particles in a fluid, #+ as they are buffeted by random currents and collisions. #+ This is colloquially known as the "Drunkard's Walk." # It can also be considered as a stripped-down simulation of a #+ Galton Board, a slanted board with a pattern of pegs, #+ down which rolls a succession of marbles, one at a time. #+ At the bottom is a row of slots or catch basins in which #+ the marbles come to rest at the end of their journey. # Think of it as a kind of bare-bones Pachinko game. # As you see by running the script, #+ most of the marbles cluster around the center slot. #+ This is consistent with the expected binomial distribution. # As a Galton Board simulation, the script #+ disregards such parameters as #+ board tilt-angle, rolling friction of the marbles, #+ angles of impact, and elasticity of the pegs. # To what extent does this affect the accuracy of the simulation? # ---------------------------------------------------------------- PASSES=500 # Number of particle interactions / marbles. ROWS=10 # Number of "collisions" (or horiz. peg rows). RANGE=3 # 0 - 2 output range from \$RANDOM. POS=0 # Left/right position. RANDOM=\$\$ # Seeds the random number generator from PID #+ of script. declare -a Slots # Array holding cumulative results of passes. NUMSLOTS=21 # Number of slots at bottom of board. Initialize_Slots () { # Zero out all elements of the array. for i in \$( seq \$NUMSLOTS ) do Slots[\$i]=0 done echo # Blank line at beginning of run. } Show_Slots () { echo -n " " for i in \$( seq \$NUMSLOTS ) # Pretty-print array elements. do printf "%3d" \${Slots[\$i]} # Allot three spaces per result. done echo # Row of slots: echo " |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|" echo " ^^" echo # Note that if the count within any particular slot exceeds 99, #+ it messes up the display. # Running only(!) 500 passes usually avoids this. } Move () { # Move one unit right / left, or stay put. Move=\$RANDOM # How random is \$RANDOM? Well, let's see ... let "Move %= RANGE" # Normalize into range of 0 - 2. case "\$Move" in 0 ) ;; # Do nothing, i.e., stay in place. 1 ) ((POS--));; # Left. 2 ) ((POS++));; # Right. * ) echo -n "Error ";; # Anomaly! (Should never occur.) esac } Play () { # Single pass (inner loop). i=0 while [ "\$i" -lt "\$ROWS" ] # One event per row. do Move ((i++)); done SHIFT=11 # Why 11, and not 10? let "POS += \$SHIFT" # Shift "zero position" to center. (( Slots[\$POS]++ )) # DEBUG: echo \$POS } Run () { # Outer loop. p=0 while [ "\$p" -lt "\$PASSES" ] do Play (( p++ )) POS=0 # Reset to zero. Why? done } # -------------- # main () Initialize_Slots Run Show_Slots # -------------- exit \$? # Exercises: # --------- # 1) Show the results in a vertical bar graph, or as an alternative, #+ a scattergram. # 2) Alter the script to use /dev/urandom instead of \$RANDOM. # Will this make the results more random?```
Jipe points out a set of techniques for generating random numbers within a range.
 ```# Generate random number between 6 and 30. rnumber=\$((RANDOM%25+6)) # Generate random number in the same 6 - 30 range, #+ but the number must be evenly divisible by 3. rnumber=\$(((RANDOM%30/3+1)*3)) # Note that this will not work all the time. # It fails if \$RANDOM%30 returns 0. # Frank Wang suggests the following alternative: rnumber=\$(( RANDOM%27/3*3+6 ))```
Bill Gradwohl came up with an improved formula that works for positive numbers.
 `rnumber=\$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))`

Here Bill presents a versatile function that returns a random number between two specified values.

Example 9-29. Random between values

 ```#!/bin/bash # random-between.sh # Random number between two specified values. # Script by Bill Gradwohl, with minor modifications by the document author. # Used with permission. randomBetween() { # Generates a positive or negative random number #+ between \$min and \$max #+ and divisible by \$divisibleBy. # Gives a "reasonably random" distribution of return values. # # Bill Gradwohl - Oct 1, 2003 syntax() { # Function embedded within function. echo echo "Syntax: randomBetween [min] [max] [multiple]" echo echo -n "Expects up to 3 passed parameters, " echo "but all are completely optional." echo "min is the minimum value" echo "max is the maximum value" echo -n "multiple specifies that the answer must be " echo "a multiple of this value." echo " i.e. answer must be evenly divisible by this number." echo echo "If any value is missing, defaults area supplied as: 0 32767 1" echo -n "Successful completion returns 0, " echo "unsuccessful completion returns" echo "function syntax and 1." echo -n "The answer is returned in the global variable " echo "randomBetweenAnswer" echo -n "Negative values for any passed parameter are " echo "handled correctly." } local min=\${1:-0} local max=\${2:-32767} local divisibleBy=\${3:-1} # Default values assigned, in case parameters not passed to function. local x local spread # Let's make sure the divisibleBy value is positive. [ \${divisibleBy} -lt 0 ] && divisibleBy=\$((0-divisibleBy)) # Sanity check. if [ \$# -gt 3 -o \${divisibleBy} -eq 0 -o \${min} -eq \${max} ]; then syntax return 1 fi # See if the min and max are reversed. if [ \${min} -gt \${max} ]; then # Swap them. x=\${min} min=\${max} max=\${x} fi # If min is itself not evenly divisible by \$divisibleBy, #+ then fix the min to be within range. if [ \$((min/divisibleBy*divisibleBy)) -ne \${min} ]; then if [ \${min} -lt 0 ]; then min=\$((min/divisibleBy*divisibleBy)) else min=\$((((min/divisibleBy)+1)*divisibleBy)) fi fi # If max is itself not evenly divisible by \$divisibleBy, #+ then fix the max to be within range. if [ \$((max/divisibleBy*divisibleBy)) -ne \${max} ]; then if [ \${max} -lt 0 ]; then max=\$((((max/divisibleBy)-1)*divisibleBy)) else max=\$((max/divisibleBy*divisibleBy)) fi fi # --------------------------------------------------------------------- # Now, to do the real work. # Note that to get a proper distribution for the end points, #+ the range of random values has to be allowed to go between #+ 0 and abs(max-min)+divisibleBy, not just abs(max-min)+1. # The slight increase will produce the proper distribution for the #+ end points. # Changing the formula to use abs(max-min)+1 will still produce #+ correct answers, but the randomness of those answers is faulty in #+ that the number of times the end points (\$min and \$max) are returned #+ is considerably lower than when the correct formula is used. # --------------------------------------------------------------------- spread=\$((max-min)) # Omair Eshkenazi points out that this test is unnecessary, #+ since max and min have already been switched around. [ \${spread} -lt 0 ] && spread=\$((0-spread)) let spread+=divisibleBy randomBetweenAnswer=\$(((RANDOM%spread)/divisibleBy*divisibleBy+min)) return 0 # However, Paulo Marcel Coelho Aragao points out that #+ when \$max and \$min are not divisible by \$divisibleBy, #+ the formula fails. # # He suggests instead the following formula: # rnumber = \$(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy)) } # Let's test the function. min=-14 max=20 divisibleBy=3 # Generate an array of expected answers and check to make sure we get #+ at least one of each answer if we loop long enough. declare -a answer minimum=\${min} maximum=\${max} if [ \$((minimum/divisibleBy*divisibleBy)) -ne \${minimum} ]; then if [ \${minimum} -lt 0 ]; then minimum=\$((minimum/divisibleBy*divisibleBy)) else minimum=\$((((minimum/divisibleBy)+1)*divisibleBy)) fi fi # If max is itself not evenly divisible by \$divisibleBy, #+ then fix the max to be within range. if [ \$((maximum/divisibleBy*divisibleBy)) -ne \${maximum} ]; then if [ \${maximum} -lt 0 ]; then maximum=\$((((maximum/divisibleBy)-1)*divisibleBy)) else maximum=\$((maximum/divisibleBy*divisibleBy)) fi fi # We need to generate only positive array subscripts, #+ so we need a displacement that that will guarantee #+ positive results. disp=\$((0-minimum)) for ((i=\${minimum}; i<=\${maximum}; i+=divisibleBy)); do answer[i+disp]=0 done # Now loop a large number of times to see what we get. loopIt=1000 # The script author suggests 100000, #+ but that takes a good long while. for ((i=0; i<\${loopIt}; ++i)); do # Note that we are specifying min and max in reversed order here to #+ make the function correct for this case. randomBetween \${max} \${min} \${divisibleBy} # Report an error if an answer is unexpected. [ \${randomBetweenAnswer} -lt \${min} -o \${randomBetweenAnswer} -gt \${max} ] \ && echo MIN or MAX error - \${randomBetweenAnswer}! [ \$((randomBetweenAnswer%\${divisibleBy})) -ne 0 ] \ && echo DIVISIBLE BY error - \${randomBetweenAnswer}! # Store the answer away statistically. answer[randomBetweenAnswer+disp]=\$((answer[randomBetweenAnswer+disp]+1)) done # Let's check the results for ((i=\${minimum}; i<=\${maximum}; i+=divisibleBy)); do [ \${answer[i+displacement]} -eq 0 ] \ && echo "We never got an answer of \$i." \ || echo "\${i} occurred \${answer[i+displacement]} times." done exit 0```

Just how random is `\$RANDOM`? The best way to test this is to write a script that tracks the distribution of "random" numbers generated by `\$RANDOM`. Let's roll a `\$RANDOM` die a few times . . .

Example 9-30. Rolling a single die with RANDOM

 ```#!/bin/bash # How random is RANDOM? RANDOM=\$\$ # Reseed the random number generator using script process ID. PIPS=6 # A die has 6 pips. MAXTHROWS=600 # Increase this if you have nothing better to do with your time. throw=0 # Throw count. ones=0 # Must initialize counts to zero, twos=0 #+ since an uninitialized variable is null, not zero. threes=0 fours=0 fives=0 sixes=0 print_result () { echo echo "ones = \$ones" echo "twos = \$twos" echo "threes = \$threes" echo "fours = \$fours" echo "fives = \$fives" echo "sixes = \$sixes" echo } update_count() { case "\$1" in 0) let "ones += 1";; # Since die has no "zero", this corresponds to 1. 1) let "twos += 1";; # And this to 2, etc. 2) let "threes += 1";; 3) let "fours += 1";; 4) let "fives += 1";; 5) let "sixes += 1";; esac } echo while [ "\$throw" -lt "\$MAXTHROWS" ] do let "die1 = RANDOM % \$PIPS" update_count \$die1 let "throw += 1" done print_result exit 0 # The scores should distribute fairly evenly, assuming RANDOM is fairly random. # With \$MAXTHROWS at 600, all should cluster around 100, plus-or-minus 20 or so. # # Keep in mind that RANDOM is a pseudorandom generator, #+ and not a spectacularly good one at that. # Randomness is a deep and complex subject. # Sufficiently long "random" sequences may exhibit #+ chaotic and other "non-random" behavior. # Exercise (easy): # --------------- # Rewrite this script to flip a coin 1000 times. # Choices are "HEADS" and "TAILS".```

As we have seen in the last example, it is best to reseed the `RANDOM` generator each time it is invoked. Using the same seed for `RANDOM` repeats the same series of numbers.  (This mirrors the behavior of the `random()` function in C.)

Example 9-31. Reseeding RANDOM

 ```#!/bin/bash # seeding-random.sh: Seeding the RANDOM variable. MAXCOUNT=25 # How many numbers to generate. random_numbers () { count=0 while [ "\$count" -lt "\$MAXCOUNT" ] do number=\$RANDOM echo -n "\$number " let "count += 1" done } echo; echo RANDOM=1 # Setting RANDOM seeds the random number generator. random_numbers echo; echo RANDOM=1 # Same seed for RANDOM... random_numbers # ...reproduces the exact same number series. # # When is it useful to duplicate a "random" number series? echo; echo RANDOM=2 # Trying again, but with a different seed... random_numbers # gives a different number series. echo; echo # RANDOM=\$\$ seeds RANDOM from process id of script. # It is also possible to seed RANDOM from 'time' or 'date' commands. # Getting fancy... SEED=\$(head -1 /dev/urandom | od -N 1 | awk '{ print \$2 }') # Pseudo-random output fetched #+ from /dev/urandom (system pseudo-random device-file), #+ then converted to line of printable (octal) numbers by "od", #+ finally "awk" retrieves just one number for SEED. RANDOM=\$SEED random_numbers echo; echo exit 0```

The `/dev/urandom` pseudo-device file provides a method of generating much more "random" pseudorandom numbers than the `\$RANDOM` variable. ```dd if=/dev/urandom of=targetfile bs=1 count=XX``` creates a file of well-scattered pseudorandom numbers. However, assigning these numbers to a variable in a script requires a workaround, such as filtering through od (as in above example, Example 15-14, and Example A-38), or even piping to md5sum (see Example 33-14).

There are also other ways to generate pseudorandom numbers in a script. Awk provides a convenient means of doing this.

Example 9-32. Pseudorandom numbers, using awk

 ```#!/bin/bash # random2.sh: Returns a pseudorandom number in the range 0 - 1. # Uses the awk rand() function. AWKSCRIPT=' { srand(); print rand() } ' # Command(s) / parameters passed to awk # Note that srand() reseeds awk's random number generator. echo -n "Random number between 0 and 1 = " echo | awk "\$AWKSCRIPT" # What happens if you leave out the 'echo'? exit 0 # Exercises: # --------- # 1) Using a loop construct, print out 10 different random numbers. # (Hint: you must reseed the "srand()" function with a different seed #+ in each pass through the loop. What happens if you fail to do this?) # 2) Using an integer multiplier as a scaling factor, generate random numbers #+ in the range between 10 and 100. # 3) Same as exercise #2, above, but generate random integers this time.```

The date command also lends itself to generating pseudorandom integer sequences.

### Notes

  True "randomness," insofar as it exists at all, can only be found in certain incompletely understood natural phenomena, such as radioactive decay. Computers only simulate randomness, and computer-generated sequences of "random" numbers are therefore referred to as pseudorandom.  The seed of a computer-generated pseudorandom number series can be considered an identification label. For example, think of the pseudorandom series with a seed of 23 as ```Series #23```.A property of a pseurandom number series is the length of the cycle before it starts repeating itself. A good pseurandom generator will produce series with very long cycles.