1315 lines
26 KiB
HTML
1315 lines
26 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<HTML
|
|
><HEAD
|
|
><TITLE
|
|
>$RANDOM: generate random integer</TITLE
|
|
><META
|
|
NAME="GENERATOR"
|
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
|
|
REL="HOME"
|
|
TITLE="Advanced Bash-Scripting Guide"
|
|
HREF="index.html"><LINK
|
|
REL="UP"
|
|
TITLE="Another Look at Variables"
|
|
HREF="variables2.html"><LINK
|
|
REL="PREVIOUS"
|
|
TITLE="Typing variables: declare or
|
|
typeset"
|
|
HREF="declareref.html"><LINK
|
|
REL="NEXT"
|
|
TITLE="Manipulating Variables"
|
|
HREF="manipulatingvars.html"></HEAD
|
|
><BODY
|
|
CLASS="SECT1"
|
|
BGCOLOR="#FFFFFF"
|
|
TEXT="#000000"
|
|
LINK="#0000FF"
|
|
VLINK="#840084"
|
|
ALINK="#0000FF"
|
|
><DIV
|
|
CLASS="NAVHEADER"
|
|
><TABLE
|
|
SUMMARY="Header navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TH
|
|
COLSPAN="3"
|
|
ALIGN="center"
|
|
>Advanced Bash-Scripting Guide: </TH
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="left"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="declareref.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="80%"
|
|
ALIGN="center"
|
|
VALIGN="bottom"
|
|
>Chapter 9. Another Look at Variables</TD
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="right"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="manipulatingvars.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"></DIV
|
|
><DIV
|
|
CLASS="SECT1"
|
|
><H1
|
|
CLASS="SECT1"
|
|
><A
|
|
NAME="RANDOMVAR"
|
|
></A
|
|
>9.3. $RANDOM: generate random integer</H1
|
|
><TABLE
|
|
BORDER="0"
|
|
WIDTH="100%"
|
|
CELLSPACING="0"
|
|
CELLPADDING="0"
|
|
CLASS="EPIGRAPH"
|
|
><TR
|
|
><TD
|
|
WIDTH="45%"
|
|
> </TD
|
|
><TD
|
|
WIDTH="45%"
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
><I
|
|
><P
|
|
><I
|
|
>Anyone who attempts to generate random numbers by
|
|
deterministic means is, of course, living in a state of
|
|
sin.</I
|
|
></P
|
|
><P
|
|
><I
|
|
>--John von Neumann</I
|
|
></P
|
|
></I
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><P
|
|
><A
|
|
NAME="RANDOMVAR01"
|
|
></A
|
|
></P
|
|
><P
|
|
><TT
|
|
CLASS="VARNAME"
|
|
>$RANDOM</TT
|
|
> is an internal Bash <A
|
|
HREF="functions.html#FUNCTIONREF"
|
|
>function</A
|
|
> (not a constant) that
|
|
returns a <I
|
|
CLASS="FIRSTTERM"
|
|
>pseudorandom</I
|
|
>
|
|
|
|
<A
|
|
NAME="AEN5817"
|
|
HREF="#FTN.AEN5817"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[1]</SPAN
|
|
></A
|
|
>
|
|
|
|
integer in the range 0 - 32767. It should
|
|
<TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>not</I
|
|
></TT
|
|
> be used to generate an encryption
|
|
key.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EX21"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-11. Generating random numbers</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/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</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="PICKCARD"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-12. Picking a random card from a deck</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/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</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="BROWNIANREF"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="BROWNIAN"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-13. Brownian Motion Simulation</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/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; echo
|
|
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
|
|
|
|
# echo -n "$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?
|
|
# 3) Provide some sort of "animation" or graphic output
|
|
# for each marble played.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
> <EM
|
|
>Jipe</EM
|
|
> points out a set of techniques for
|
|
generating random numbers within a range.
|
|
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
># 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 ))</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
><P
|
|
> <EM
|
|
>Bill Gradwohl</EM
|
|
> came up with an improved
|
|
formula that works for positive numbers.
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
><P
|
|
>Here Bill presents a versatile function that returns
|
|
a random number between two specified values.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="RANDOMBETWEEN"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-14. Random between values</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# random-between.sh
|
|
# Random number between two specified values.
|
|
# Script by Bill Gradwohl, with minor modifications by the document author.
|
|
# Corrections in lines 187 and 189 by Anthony Le Clezio.
|
|
# 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+disp]} -eq 0 ] \
|
|
&& echo "We never got an answer of $i." \
|
|
|| echo "${i} occurred ${answer[i+disp]} times."
|
|
done
|
|
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>Just how random is <TT
|
|
CLASS="VARNAME"
|
|
>$RANDOM</TT
|
|
>? The best
|
|
way to test this is to write a script that tracks
|
|
the distribution of <SPAN
|
|
CLASS="QUOTE"
|
|
>"random"</SPAN
|
|
> numbers
|
|
generated by <TT
|
|
CLASS="VARNAME"
|
|
>$RANDOM</TT
|
|
>. Let's roll a
|
|
<TT
|
|
CLASS="VARNAME"
|
|
>$RANDOM</TT
|
|
> die a few times . . .</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="RANDOMTEST"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-15. Rolling a single die with RANDOM</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/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 # Number of times the dice have been cast.
|
|
|
|
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) ((ones++));; # Since a die has no "zero", this corresponds to 1.
|
|
1) ((twos++));; # And this to 2.
|
|
2) ((threes++));; # And so forth.
|
|
3) ((fours++));;
|
|
4) ((fives++));;
|
|
5) ((sixes++));;
|
|
esac
|
|
}
|
|
|
|
echo
|
|
|
|
|
|
while [ "$throw" -lt "$MAXTHROWS" ]
|
|
do
|
|
let "die1 = RANDOM % $PIPS"
|
|
update_count $die1
|
|
let "throw += 1"
|
|
done
|
|
|
|
print_result
|
|
|
|
exit $?
|
|
|
|
# The scores should distribute evenly, assuming RANDOM is 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."</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>As we have seen in the last example, it is best to
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>reseed</I
|
|
> the <TT
|
|
CLASS="PARAMETER"
|
|
><I
|
|
>RANDOM</I
|
|
></TT
|
|
>
|
|
generator each time it is invoked. Using the same seed
|
|
for <TT
|
|
CLASS="PARAMETER"
|
|
><I
|
|
>RANDOM</I
|
|
></TT
|
|
> repeats the same series
|
|
of numbers.
|
|
<A
|
|
NAME="AEN5857"
|
|
HREF="#FTN.AEN5857"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[2]</SPAN
|
|
></A
|
|
>
|
|
(This mirrors the behavior of the
|
|
<TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>random()</I
|
|
></TT
|
|
> function in
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>C</I
|
|
>.)</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="SEEDINGRANDOM"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-16. Reseeding RANDOM</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# seeding-random.sh: Seeding the RANDOM variable.
|
|
# v 1.1, reldate 09 Feb 2013
|
|
|
|
MAXCOUNT=25 # How many numbers to generate.
|
|
SEED=
|
|
|
|
random_numbers ()
|
|
{
|
|
local count=0
|
|
local number
|
|
|
|
while [ "$count" -lt "$MAXCOUNT" ]
|
|
do
|
|
number=$RANDOM
|
|
echo -n "$number "
|
|
let "count++"
|
|
done
|
|
}
|
|
|
|
echo; echo
|
|
|
|
SEED=1
|
|
RANDOM=$SEED # Setting RANDOM seeds the random number generator.
|
|
echo "Random seed = $SEED"
|
|
random_numbers
|
|
|
|
|
|
RANDOM=$SEED # Same seed for RANDOM . . .
|
|
echo; echo "Again, with same random seed ..."
|
|
echo "Random seed = $SEED"
|
|
random_numbers # . . . reproduces the exact same number series.
|
|
#
|
|
# When is it useful to duplicate a "random" series?
|
|
|
|
echo; echo
|
|
|
|
SEED=2
|
|
RANDOM=$SEED # Trying again, but with a different seed . . .
|
|
echo "Random seed = $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 }'| sed s/^0*//)
|
|
# Pseudo-random output fetched
|
|
#+ from /dev/urandom (system pseudo-random device-file),
|
|
#+ then converted to line of printable (octal) numbers by "od",
|
|
#+ then "awk" retrieves just one number for SEED,
|
|
#+ finally "sed" removes any leading zeros.
|
|
RANDOM=$SEED
|
|
echo "Random seed = $SEED"
|
|
random_numbers
|
|
|
|
echo; echo
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="URANDOMREF"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="NOTE"
|
|
><P
|
|
></P
|
|
><TABLE
|
|
CLASS="NOTE"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="25"
|
|
ALIGN="CENTER"
|
|
VALIGN="TOP"
|
|
><IMG
|
|
SRC="../images/note.gif"
|
|
HSPACE="5"
|
|
ALT="Note"></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
><P
|
|
>The <TT
|
|
CLASS="FILENAME"
|
|
>/dev/urandom</TT
|
|
> pseudo-device file
|
|
provides a method of generating much more <SPAN
|
|
CLASS="QUOTE"
|
|
>"random"</SPAN
|
|
>
|
|
pseudorandom numbers than the <TT
|
|
CLASS="VARNAME"
|
|
>$RANDOM</TT
|
|
>
|
|
variable. <TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>dd if=/dev/urandom of=targetfile
|
|
bs=1 count=XX</B
|
|
></TT
|
|
> 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 <A
|
|
HREF="extmisc.html#ODREF"
|
|
>od</A
|
|
>
|
|
(as in above example, <A
|
|
HREF="textproc.html#RND"
|
|
>Example 16-14</A
|
|
>, and
|
|
<A
|
|
HREF="contributed-scripts.html#INSERTIONSORT"
|
|
>Example A-36</A
|
|
>), or even piping to
|
|
<A
|
|
HREF="filearchiv.html#MD5SUMREF"
|
|
>md5sum</A
|
|
> (see <A
|
|
HREF="colorizing.html#HORSERACE"
|
|
>Example 36-16</A
|
|
>).</P
|
|
><P
|
|
><A
|
|
NAME="AWKRANDOMREF"
|
|
></A
|
|
></P
|
|
><P
|
|
>There are also other ways to generate pseudorandom
|
|
numbers in a script. <B
|
|
CLASS="COMMAND"
|
|
>Awk</B
|
|
> provides a
|
|
convenient means of doing this.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="RANDOM2"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 9-17. Pseudorandom numbers, using <A
|
|
HREF="awk.html#AWKREF"
|
|
>awk</A
|
|
></B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# random2.sh: Returns a pseudorandom number in the range 0 - 1,
|
|
#+ to 6 decimal places. For example: 0.822725
|
|
# 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 omit this?)
|
|
|
|
# 2) Using an integer multiplier as a scaling factor, generate random numbers
|
|
#+ in the range of 10 to 100.
|
|
|
|
# 3) Same as exercise #2, above, but generate random integers this time.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>The <A
|
|
HREF="timedate.html#DATEREF"
|
|
>date</A
|
|
> command also lends
|
|
itself to <A
|
|
HREF="timedate.html#DATERANDREF"
|
|
>generating pseudorandom
|
|
integer sequences</A
|
|
>.</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></DIV
|
|
><H3
|
|
CLASS="FOOTNOTES"
|
|
>Notes</H3
|
|
><TABLE
|
|
BORDER="0"
|
|
CLASS="FOOTNOTES"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="5%"
|
|
><A
|
|
NAME="FTN.AEN5817"
|
|
HREF="randomvar.html#AEN5817"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[1]</SPAN
|
|
></A
|
|
></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="95%"
|
|
><P
|
|
>True <SPAN
|
|
CLASS="QUOTE"
|
|
>"randomness,"</SPAN
|
|
> insofar as
|
|
it exists at all, can only be found in certain incompletely
|
|
understood natural phenomena, such as radioactive
|
|
decay. Computers only <I
|
|
CLASS="FIRSTTERM"
|
|
>simulate</I
|
|
>
|
|
randomness, and computer-generated sequences of
|
|
<SPAN
|
|
CLASS="QUOTE"
|
|
>"random"</SPAN
|
|
> numbers are therefore referred to as
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>pseudorandom</I
|
|
>.</P
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="5%"
|
|
><A
|
|
NAME="FTN.AEN5857"
|
|
HREF="randomvar.html#AEN5857"
|
|
><SPAN
|
|
CLASS="footnote"
|
|
>[2]</SPAN
|
|
></A
|
|
></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
WIDTH="95%"
|
|
><P
|
|
>The <I
|
|
CLASS="FIRSTTERM"
|
|
>seed</I
|
|
> of a
|
|
computer-generated pseudorandom number series
|
|
can be considered an identification label. For
|
|
example, think of the pseudorandom series with a
|
|
seed of <EM
|
|
>23</EM
|
|
> as <TT
|
|
CLASS="REPLACEABLE"
|
|
><I
|
|
>Series
|
|
#23</I
|
|
></TT
|
|
>.</P
|
|
><P
|
|
>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.</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><DIV
|
|
CLASS="NAVFOOTER"
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"><TABLE
|
|
SUMMARY="Footer navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="declareref.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="index.html"
|
|
ACCESSKEY="H"
|
|
>Home</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="manipulatingvars.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
>Typing variables: <B
|
|
CLASS="COMMAND"
|
|
>declare</B
|
|
> or
|
|
<B
|
|
CLASS="COMMAND"
|
|
>typeset</B
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="variables2.html"
|
|
ACCESSKEY="U"
|
|
>Up</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
>Manipulating Variables</TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></BODY
|
|
></HTML
|
|
> |