randomvar.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 1,064 行 · 第 1/2 页
HTML
1,064 行
149 for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do150 answer[i+displacement]=0151 done152 153 154 # 现在, 让我们循环足够多的次数, 来得到我们想要的答案.155 loopIt=1000 # 脚本作者建议循环 100000 次,156 #+ 但是这需要的时间太长了.157 158 for ((i=0; i<${loopIt}; ++i)); do159 160 # 注意, 我们在这里调用randomBetweenAnswer函数时, 估计将min和max颠倒顺序.161 #+ 这是为了测试在这种情况下, 此函数是否还能正确的运行.162 163 randomBetween ${max} ${min} ${divisibleBy}164 165 # 如果答案不是我们所期望的, 就报错.166 [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] && echo MIN or MAX error - ${randomBetweenAnswer}!167 [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] && echo DIVISIBLE BY error - ${randomBetweenAnswer}!168 169 # 将统计值保存到answer中.170 answer[randomBetweenAnswer+displacement]=$((answer[randomBetweenAnswer+displacement]+1))171 done172 173 174 175 # 让我们来察看一下结果.176 177 for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do178 [ ${answer[i+displacement]} -eq 0 ] && echo "We never got an answer of $i." || echo "${i} occurred ${answer[i+displacement]} times."179 done180 181 182 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P><CODECLASS="VARNAME">$RANDOM</CODE>到底有多随机? 最好的方法就是编写脚本来测试一下, 跟踪<CODECLASS="VARNAME">$RANDOM</CODE>所产生的<SPANCLASS="QUOTE">"随机"</SPAN>数的分布情况. 让我们用<CODECLASS="VARNAME">$RANDOM</CODE>来摇骰子. . .</P><DIVCLASS="EXAMPLE"><HR><ANAME="RANDOMTEST"></A><P><B>例子 9-28. 用随机数来摇单个骰子</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # RANDOM到底有多随机? 3 4 RANDOM=$$ # 使用脚本的进程ID来作为随机数的种子. 5 6 PIPS=6 # 一个骰子有6个面. 7 MAXTHROWS=600 # 如果你没别的事做, 可以增加这个数值. 8 throw=0 # 抛骰子的次数. 9 10 ones=0 # 必须把所有的count都初始化为0, 11 twos=0 #+ 因为未初始化的变量为null, 不是0. 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";; # 因为骰子没有"零", 所以给1. 33 1) let "twos += 1";; # 把这个设为2, 后边也一样. 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 # 如果RANDOM是真正的随机, 那么摇出来结果应该是平均的. 56 # 把$MAXTHROWS设为600, 那么每个面应该是100, 上下的出入不应该超过20. 57 # 58 # 记住RANDOM毕竟是一个伪随机数, 59 #+ 并且不是十分完美. 60 61 # 随机数的生成是一个十分深奥并复杂的问题. 62 # 足够长的随机序列, 不但会展现其杂乱无章的一面, 63 #+ 同样也会展现其机会均等的一面. 64 65 # 练习 (很简单): 66 # -------------- 67 # 重写这个脚本, 做成抛1000次硬币的形式. 68 # 分为"头"和"字"两面.</PRE></FONT></TD></TR></TABLE><HR></DIV><P>就像我们在上边的例子中所看到的, 最好在每次产生<CODECLASS="VARNAME">RANDOM</CODE>的时候都使用新的种子. 因为如果使用同样种子的话, 那么<CODECLASS="VARNAME">RANDOM</CODE>将会产生相同的序列. <ANAME="AEN5309"HREF="#FTN.AEN5309"><SPANCLASS="footnote">[2]</SPAN></A> (<ICLASS="FIRSTTERM">C</I>语言中的<TTCLASS="REPLACEABLE"><I>random()</I></TT>函数也会有这样的行为.) </P><DIVCLASS="EXAMPLE"><HR><ANAME="SEEDINGRANDOM"></A><P><B>例子 9-29. 重新分配随机数种子</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # seeding-random.sh: 设置RANDOM变量作为种子. 3 4 MAXCOUNT=25 # 决定产生多少个随机数. 5 6 random_numbers () 7 { 8 count=0 9 while [ "$count" -lt "$MAXCOUNT" ] 10 do 11 number=$RANDOM 12 echo -n "$number " 13 let "count += 1" 14 done 15 } 16 17 echo; echo 18 19 RANDOM=1 # 为随机数的产生来设置RANDOM种子. 20 random_numbers 21 22 echo; echo 23 24 RANDOM=1 # 设置同样的种子... 25 random_numbers # ...将会和上边产生的随机序列相同. 26 # 27 # 复制一个相同的"随机"序列在什么情况下有用呢? 28 29 echo; echo 30 31 RANDOM=2 # 在试一次, 但是这次使用不同的种子... 32 random_numbers # 这次将得到一个不同的随机序列. 33 34 echo; echo 35 36 # RANDOM=$$ 使用脚本的进程ID来作为产生随机数的种子. 37 # 从 'time' 或 'date' 命令中取得RANDOM作为种子也是常用的做法. 38 39 # 一个很有想象力的方法... 40 SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }') 41 # 首先从/dev/urandom(系统伪随机设备文件)中取出一行, 42 #+ 然后将这个可打印行转换为8进制数, 使用"od"命令来转换. 43 #+ 最后使用"awk"来获得一个数, 44 #+ 这个数将作为产生随机数的种子. 45 RANDOM=$SEED 46 random_numbers 47 48 echo; echo 49 50 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P><ANAME="URANDOMREF"></A></P><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P><TTCLASS="FILENAME">/dev/urandom</TT>设备文件提供了一种比单独使用$RANDOM更好的, 能够产生更加<SPANCLASS="QUOTE">"随机"</SPAN>的随机数的方法. <KBDCLASS="USERINPUT">dd if=/dev/urandom of=targetfile bs=1 count=XX</KBD>能够产生一个很分散的伪随机数序列. 然而, 如果想要将这个数赋值到一个脚本文件的变量中, 还需要可操作性, 比如使用<AHREF="extmisc.html#ODREF">od</A>命令 (就像上边的例子, 还有<AHREF="textproc.html#RND">例子 12-13</A>), 或者使用<AHREF="extmisc.html#DDREF">dd</A>命令 (参见<AHREF="extmisc.html#BLOTOUT">例子 12-55</A>), 或者通过管道传递到<AHREF="filearchiv.html#MD5SUMREF">md5sum</A>命令中 (参见<AHREF="colorizing.html#HORSERACE">例子 33-14</A>).</P><P><ANAME="AWKRANDOMREF"></A></P><P>当然还有其他的产生伪随机数的方法. <BCLASS="COMMAND">awk</B>就能提供一个方便的方法来做到这点. </P><DIVCLASS="EXAMPLE"><HR><ANAME="RANDOM2"></A><P><B>例子 9-30. 使用<AHREF="awk.html#AWKREF">awk</A>来产生伪随机数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # random2.sh: 产生一个范围在 0 - 1 之间的伪随机数. 3 # 使用了awk的rand()函数. 4 5 AWKSCRIPT=' { srand(); print rand() } ' 6 # Command(s) / 传递到awk中的参数 7 # 注意, srand()是awk中用来产生伪随机数种子的函数. 8 9 10 echo -n "Random number between 0 and 1 = " 11 12 echo | awk "$AWKSCRIPT" 13 # 如果你省去'echo', 会怎样? 14 15 exit 0 16 17 18 # 练习: 19 # ----- 20 21 # 1) 使用循环结构, 打印出10个不同的随机数. 22 # (提示: 在每次循环过程中, 你必须使用"srand()"函数来生成不同的种子, 23 #+ 如果你不这么做会怎样?) 24 25 # 2) 使用整数乘法作为一个比例因子, 在10到100的范围之间, 26 #+ 来产生随机数. 27 28 # 3) 同上边的练习#2, 但是这次产生随机整数.</PRE></FONT></TD></TR></TABLE><HR></DIV><P><AHREF="timedate.html#DATEREF">date</A>命令也可以用来<AHREF="timedate.html#DATERANDREF">产生伪随机整数序列</A>. </P></TD></TR></TABLE></DIV></DIV><H3CLASS="FOOTNOTES">注意事项</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN5276"HREF="randomvar.html#AEN5276"><SPANCLASS="footnote">[1]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>真正的<SPANCLASS="QUOTE">"随机事件, "</SPAN>在它存在的范围内, 只发生在特定的几个未知的自然界现象中, 比如放射性衰变. 计算机只能产生模拟的随机事件, 并且计算机产生的<SPANCLASS="QUOTE">"随机"</SPAN>数只能称为<EM>伪随机数</EM>. </P></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN5309"HREF="randomvar.html#AEN5309"><SPANCLASS="footnote">[2]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>计算机用来产生伪随机数的<ICLASS="FIRSTTERM">种子</I>可以被看成是一种标识标签. 比如, 使用种子<EM>23</EM>所产生的随机序列就被称为<EM>序列 #23</EM>. </P><P>一个伪随机序列的特点就是在这个序列开始重复之前的所有元素个数的总和, 也就是这个序列的长度. 一个好的伪随机产生算法可以产生一个非常长的不重复序列. </P></TD></TR></TABLE><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="ivr.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html"ACCESSKEY="H">首页</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="dblparens.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">变量的间接引用</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="variables2.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">双圆括号结构</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?