📄 randomvar.html
字号:
73 } 74 75 76 Play () { # Single pass (inner loop). 77 i=0 78 while [ "$i" -lt "$ROWS" ] # One event per row. 79 do 80 Move 81 ((i++)); 82 done 83 84 SHIFT=11 # Why 11, and not 10? 85 let "POS += $SHIFT" # Shift "zero position" to center. 86 (( Slots[$POS]++ )) # DEBUG: echo $POS 87 } 88 89 90 Run () { # Outer loop. 91 p=0 92 while [ "$p" -lt "$PASSES" ] 93 do 94 Play 95 (( p++ )) 96 POS=0 # Reset to zero. Why? 97 done 98 } 99 100 101 # -------------- 102 # main () 103 Initialize_Slots 104 Run 105 Show_Slots 106 # -------------- 107 108 exit $? 109 110 # Exercises: 111 # --------- 112 # 1) Show the results in a vertical bar graph, or as an alternative, 113 #+ a scattergram. 114 # 2) Alter the script to use /dev/urandom instead of $RANDOM. 115 # Will this make the results more random?</PRE></TD></TR></TABLE><HR></DIV><P> <SPANCLASS="emphasis"><ICLASS="EMPHASIS">Jipe</I></SPAN> points out a set of techniques for generating random numbers within a range. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 # Generate random number between 6 and 30. 2 rnumber=$((RANDOM%25+6)) 3 4 # Generate random number in the same 6 - 30 range, 5 #+ but the number must be evenly divisible by 3. 6 rnumber=$(((RANDOM%30/3+1)*3)) 7 8 # Note that this will not work all the time. 9 # It fails if $RANDOM%30 returns 0. 10 11 # Frank Wang suggests the following alternative: 12 rnumber=$(( RANDOM%27/3*3+6 ))</PRE></TD></TR></TABLE> </P><P> <SPANCLASS="emphasis"><ICLASS="EMPHASIS">Bill Gradwohl</I></SPAN> came up with an improved formula that works for positive numbers. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))</PRE></TD></TR></TABLE> </P><P>Here Bill presents a versatile function that returns a random number between two specified values.</P><DIVCLASS="EXAMPLE"><HR><ANAME="RANDOMBETWEEN"></A><P><B>Example 9-29. Random between values</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # random-between.sh 3 # Random number between two specified values. 4 # Script by Bill Gradwohl, with minor modifications by the document author. 5 # Used with permission. 6 7 8 randomBetween() { 9 # Generates a positive or negative random number 10 #+ between $min and $max 11 #+ and divisible by $divisibleBy. 12 # Gives a "reasonably random" distribution of return values. 13 # 14 # Bill Gradwohl - Oct 1, 2003 15 16 syntax() { 17 # Function embedded within function. 18 echo 19 echo "Syntax: randomBetween [min] [max] [multiple]" 20 echo 21 echo -n "Expects up to 3 passed parameters, " 22 echo "but all are completely optional." 23 echo "min is the minimum value" 24 echo "max is the maximum value" 25 echo -n "multiple specifies that the answer must be " 26 echo "a multiple of this value." 27 echo " i.e. answer must be evenly divisible by this number." 28 echo 29 echo "If any value is missing, defaults area supplied as: 0 32767 1" 30 echo -n "Successful completion returns 0, " 31 echo "unsuccessful completion returns" 32 echo "function syntax and 1." 33 echo -n "The answer is returned in the global variable " 34 echo "randomBetweenAnswer" 35 echo -n "Negative values for any passed parameter are " 36 echo "handled correctly." 37 } 38 39 local min=${1:-0} 40 local max=${2:-32767} 41 local divisibleBy=${3:-1} 42 # Default values assigned, in case parameters not passed to function. 43 44 local x 45 local spread 46 47 # Let's make sure the divisibleBy value is positive. 48 [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy)) 49 50 # Sanity check. 51 if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o ${min} -eq ${max} ]; then 52 syntax 53 return 1 54 fi 55 56 # See if the min and max are reversed. 57 if [ ${min} -gt ${max} ]; then 58 # Swap them. 59 x=${min} 60 min=${max} 61 max=${x} 62 fi 63 64 # If min is itself not evenly divisible by $divisibleBy, 65 #+ then fix the min to be within range. 66 if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then 67 if [ ${min} -lt 0 ]; then 68 min=$((min/divisibleBy*divisibleBy)) 69 else 70 min=$((((min/divisibleBy)+1)*divisibleBy)) 71 fi 72 fi 73 74 # If max is itself not evenly divisible by $divisibleBy, 75 #+ then fix the max to be within range. 76 if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then 77 if [ ${max} -lt 0 ]; then 78 max=$((((max/divisibleBy)-1)*divisibleBy)) 79 else 80 max=$((max/divisibleBy*divisibleBy)) 81 fi 82 fi 83 84 # --------------------------------------------------------------------- 85 # Now, to do the real work. 86 87 # Note that to get a proper distribution for the end points, 88 #+ the range of random values has to be allowed to go between 89 #+ 0 and abs(max-min)+divisibleBy, not just abs(max-min)+1. 90 91 # The slight increase will produce the proper distribution for the 92 #+ end points. 93 94 # Changing the formula to use abs(max-min)+1 will still produce 95 #+ correct answers, but the randomness of those answers is faulty in 96 #+ that the number of times the end points ($min and $max) are returned 97 #+ is considerably lower than when the correct formula is used. 98 # --------------------------------------------------------------------- 99 100 spread=$((max-min)) 101 # Omair Eshkenazi points out that this test is unnecessary, 102 #+ since max and min have already been switched around. 103 [ ${spread} -lt 0 ] && spread=$((0-spread)) 104 let spread+=divisibleBy 105 randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min)) 106 107 return 0 108 109 # However, Paulo Marcel Coelho Aragao points out that 110 #+ when $max and $min are not divisible by $divisibleBy, 111 #+ the formula fails. 112 # 113 # He suggests instead the following formula: 114 # rnumber = $(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy)) 115 116 } 117 118 # Let's test the function. 119 min=-14 120 max=20 121 divisibleBy=3 122 123 124 # Generate an array of expected answers and check to make sure we get 125 #+ at least one of each answer if we loop long enough. 126 127 declare -a answer 128 minimum=${min} 129 maximum=${max} 130 if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then 131 if [ ${minimum} -lt 0 ]; then 132 minimum=$((minimum/divisibleBy*divisibleBy)) 133 else 134 minimum=$((((minimum/divisibleBy)+1)*divisibleBy)) 135 fi 136 fi 137 138 139 # If max is itself not evenly divisible by $divisibleBy, 140 #+ then fix the max to be within range. 141 142 if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then 143 if [ ${maximum} -lt 0 ]; then 144 maximum=$((((maximum/divisibleBy)-1)*divisibleBy)) 145 else 146 maximum=$((maximum/divisibleBy*divisibleBy)) 147 fi 148 fi 149 150 151 # We need to generate only positive array subscripts, 152 #+ so we need a displacement that that will guarantee 153 #+ positive results. 154 155 disp=$((0-minimum)) 156 for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do 157 answer[i+disp]=0 158 done 159 160 161 # Now loop a large number of times to see what we get. 162 loopIt=1000 # The script author suggests 100000, 163 #+ but that takes a good long while. 164 165 for ((i=0; i<${loopIt}; ++i)); do 166 167 # Note that we are specifying min and max in reversed order here to 168 #+ make the function correct for this case. 169 170 randomBetween ${max} ${min} ${divisibleBy} 171 172 # Report an error if an answer is unexpected. 173 [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] \ 174 && echo MIN or MAX error - ${randomBetweenAnswer}! 175 [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] \ 176 && echo DIVISIBLE BY error - ${randomBetweenAnswer}! 177 178 # Store the answer away statistically. 179 answer[randomBetweenAnswer+disp]=$((answer[randomBetweenAnswer+disp]+1)) 180 done 181 182 183 184 # Let's check the results 185 186 for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do 187 [ ${answer[i+displacement]} -eq 0 ] \ 188 && echo "We never got an answer of $i." \ 189 || echo "${i} occurred ${answer[i+displacement]} times." 190 done 191 192 193 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>Just how random is <TTCLASS="VARNAME">$RANDOM</TT>? The best way to test this is to write a script that tracks the distribution of <SPANCLASS="QUOTE">"random"</SPAN> numbers generated by <TTCLASS="VARNAME">$RANDOM</TT>. Let's roll a <TTCLASS="VARNAME">$RANDOM</TT> die a few times . . .</P><DIVCLASS="EXAMPLE"><HR><ANAME="RANDOMTEST"></A><P><B>Example 9-30. Rolling a single die with RANDOM</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # How random is RANDOM? 3 4 RANDOM=$$ # Reseed the random number generator using script process ID. 5 6 PIPS=6 # A die has 6 pips. 7 MAXTHROWS=600 # Increase this if you have nothing better to do with your time. 8 throw=0 # Throw count. 9 10 ones=0 # Must initialize counts to zero, 11 twos=0 #+ since an uninitialized variable is null, not zero. 12 threes=0 13 fours=0 14 fives=0 15 sixes=0 16 17 print_result () 18 { 19 echo 20 echo "ones = $ones" 21 echo "twos = $twos" 22 echo "threes = $threes" 23 echo "fours = $fours" 24 echo "fives = $fives" 25 echo "sixes = $sixes" 26 echo 27 } 28 29 update_count() 30 { 31 case "$1" in 32 0) let "ones += 1";; # Since die has no "zero", this corresponds to 1. 33 1) let "twos += 1";; # And this to 2, etc. 34 2) let "threes += 1";; 35 3) let "fours += 1";; 36 4) let "fives += 1";; 37 5) let "sixes += 1";; 38 esac 39 } 40 41 echo 42 43 44 while [ "$throw" -lt "$MAXTHROWS" ] 45 do 46 let "die1 = RANDOM % $PIPS" 47 update_count $die1 48 let "throw += 1" 49 done 50 51 print_result 52 53 exit 0 54 55 # The scores should distribute fairly evenly, assuming RANDOM is fairly random. 56 # With $MAXTHROWS at 600, all should cluster around 100, plus-or-minus 20 or so. 57 # 58 # Keep in mind that RANDOM is a pseudorandom generator, 59 #+ and not a spectacularly good one at that. 60 61 # Randomness is a deep and complex subject.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -