randomvar.html

来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 1,064 行 · 第 1/2 页

HTML
1,064
字号
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><HTML><HEAD><TITLE>$RANDOM: 产生随机整数</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINKREL="HOME"TITLE="高级Bash脚本编程指南"HREF="index.html"><LINKREL="UP"TITLE="变量重游"HREF="variables2.html"><LINKREL="PREVIOUS"TITLE="变量的间接引用"HREF="ivr.html"><LINKREL="NEXT"TITLE="双圆括号结构"HREF="dblparens.html"></HEAD><BODYCLASS="SECT1"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">高级Bash脚本编程指南: 一本深入学习shell脚本艺术的书籍</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="ivr.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">9. 变量重游</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="dblparens.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="RANDOMVAR">9.6. $RANDOM: 产生随机整数</A></H1><P><CODECLASS="VARNAME">$RANDOM</CODE>是Bash的内部<AHREF="functions.html#FUNCTIONREF">函数</A> (并不是常量), 		这个函数将返回一个<ICLASS="FIRSTTERM">伪随机</I>		<ANAME="AEN5276"HREF="#FTN.AEN5276"><SPANCLASS="footnote">[1]</SPAN></A>	整数, 范围在0 - 32767之间. 它<TTCLASS="REPLACEABLE"><I>不</I></TT>应该被用来产生密匙. 	</P><DIVCLASS="EXAMPLE"><HR><ANAME="EX21"></A><P><B>例子 9-25. 产生随机整数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;  3&nbsp;# 每次调用$RANDOM都会返回不同的随机整数.   4&nbsp;# 一般范围为: 0 - 32767 (有符号的16-bit整数).  5&nbsp;  6&nbsp;MAXCOUNT=10  7&nbsp;count=1  8&nbsp;  9&nbsp;echo 10&nbsp;echo "$MAXCOUNT random numbers:" 11&nbsp;echo "-----------------" 12&nbsp;while [ "$count" -le $MAXCOUNT ]      # 产生10 ($MAXCOUNT)个随机整数. 13&nbsp;do 14&nbsp;  number=$RANDOM 15&nbsp;  echo $number 16&nbsp;  let "count += 1"  # 增加计数. 17&nbsp;done 18&nbsp;echo "-----------------" 19&nbsp; 20&nbsp;# 如果你需要在特定范围内产生随机整数, 那么使用'modulo'(模)操作.(译者注: 事实上, 这不是一个非常好的办法. 理由见man 3 rand) 21&nbsp;# 取模操作会返回除法的余数. 22&nbsp; 23&nbsp;RANGE=500 24&nbsp; 25&nbsp;echo 26&nbsp; 27&nbsp;number=$RANDOM 28&nbsp;let "number %= $RANGE" 29&nbsp;#           ^^ 30&nbsp;echo "Random number less than $RANGE  ---  $number" 31&nbsp; 32&nbsp;echo 33&nbsp; 34&nbsp; 35&nbsp; 36&nbsp;#  如果你需要产生一个大于某个下限的随机整数. 37&nbsp;#+ 那么建立一个test循环来丢弃所有小于此下限值的整数.  38&nbsp; 39&nbsp;FLOOR=200 40&nbsp; 41&nbsp;number=0   #初始化 42&nbsp;while [ "$number" -le $FLOOR ] 43&nbsp;do 44&nbsp;  number=$RANDOM 45&nbsp;done 46&nbsp;echo "Random number greater than $FLOOR ---  $number" 47&nbsp;echo 48&nbsp; 49&nbsp;   # 让我们对上边的循环尝试一个小改动, 如下: 50&nbsp;   #       let "number = $RANDOM + $FLOOR" 51&nbsp;   # 这将不再需要那个while循环, 并且能够运行的更快. 52&nbsp;   # 但是, 这可能会产生一个问题, 思考一下是什么问题? 53&nbsp; 54&nbsp; 55&nbsp; 56&nbsp;# 结合上边两个例子, 来在指定的上下限之间来产生随机数. 57&nbsp;number=0   #initialize 58&nbsp;while [ "$number" -le $FLOOR ] 59&nbsp;do 60&nbsp;  number=$RANDOM 61&nbsp;  let "number %= $RANGE"  # 让$number依比例落在$RANGE的范围内. 62&nbsp;done 63&nbsp;echo "Random number between $FLOOR and $RANGE ---  $number" 64&nbsp;echo 65&nbsp; 66&nbsp; 67&nbsp; 68&nbsp;# 产生二元值, 就是, "true" 或 "false" 两个值. 69&nbsp;BINARY=2 70&nbsp;T=1 71&nbsp;number=$RANDOM 72&nbsp; 73&nbsp;let "number %= $BINARY" 74&nbsp;#  注意 let "number &#62;&#62;= 14"    将会给出一个更好的随机分配. #(译者注: 正如man页中提到的, 更高位的随机分布更加平均) 75&nbsp;#+ (右移14位将把所有的位全部清空, 除了第15位, 因为有符号, 第16位是符号位). #取模操作使用低位来产生随机数会相对不平均) 76&nbsp;if [ "$number" -eq $T ] 77&nbsp;then 78&nbsp;  echo "TRUE" 79&nbsp;else 80&nbsp;  echo "FALSE" 81&nbsp;fi   82&nbsp; 83&nbsp;echo 84&nbsp; 85&nbsp; 86&nbsp;# 抛骰子. 87&nbsp;SPOTS=6   # 模6给出的范围是0 - 5. 88&nbsp;          # 加1会得到期望的范围1 - 6. 89&nbsp;          # 感谢, Paulo Marcel Coelho Aragao, 对此进行的简化. 90&nbsp;die1=0 91&nbsp;die2=0 92&nbsp;# 是否让SPOTS=7会比加1更好呢? 解释行或者不行的原因? 93&nbsp; 94&nbsp;# 每次抛骰子, 都会给出均等的机会. 95&nbsp; 96&nbsp;    let "die1 = $RANDOM % $SPOTS +1" # 抛第一次. 97&nbsp;    let "die2 = $RANDOM % $SPOTS +1" # 抛第二次. 98&nbsp;    #  上边的算术操作中, 哪个具有更高的优先级呢 -- 99&nbsp;    #+ 模(%) 还是加法操作(+)?100&nbsp;101&nbsp;102&nbsp;let "throw = $die1 + $die2"103&nbsp;echo "Throw of the dice = $throw"104&nbsp;echo105&nbsp;106&nbsp;107&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="PICKCARD"></A><P><B>例子 9-26. 从一幅扑克牌中取出一张随机的牌</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# pick-card.sh  3&nbsp;  4&nbsp;# 这是一个从数组中取出随机元素的一个例子.  5&nbsp;  6&nbsp;  7&nbsp;# 抽取一张牌, 任何一张.  8&nbsp;  9&nbsp;Suites="Clubs 10&nbsp;Diamonds 11&nbsp;Hearts 12&nbsp;Spades" 13&nbsp; 14&nbsp;Denominations="2 15&nbsp;3 16&nbsp;4 17&nbsp;5 18&nbsp;6 19&nbsp;7 20&nbsp;8 21&nbsp;9 22&nbsp;10 23&nbsp;Jack 24&nbsp;Queen 25&nbsp;King 26&nbsp;Ace" 27&nbsp; 28&nbsp;# 注意变量的多行展开. 29&nbsp; 30&nbsp; 31&nbsp;suite=($Suites)                # 读入一个数组. 32&nbsp;denomination=($Denominations) 33&nbsp; 34&nbsp;num_suites=${#suite[*]}        # 计算有多少个数组元素. 35&nbsp;num_denominations=${#denomination[*]} 36&nbsp; 37&nbsp;echo -n "${denomination[$((RANDOM%num_denominations))]} of " 38&nbsp;echo ${suite[$((RANDOM%num_suites))]} 39&nbsp; 40&nbsp; 41&nbsp;# $bozo sh pick-cards.sh 42&nbsp;# Jack of Clubs 43&nbsp; 44&nbsp; 45&nbsp;# 感谢, "jipe," 指出$RANDOM的这个用法. 46&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>	<EM>Jipe</EM>展示了一套技巧来在一个指定范围内产生随机数. 	<TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#  在6 到 30之间产生随机数.  2&nbsp;   rnumber=$((RANDOM%25+6))	  3&nbsp;  4&nbsp;#  还是在6 - 30之间产生随机数,  5&nbsp;#+ 但是这个数还必须能够被3整除.  6&nbsp;   rnumber=$(((RANDOM%30/3+1)*3))  7&nbsp;  8&nbsp;#  注意, 并不是在所有情况下都能正确运行.  9&nbsp;#  如果$RANDOM返回0, 那么就会失败. 10&nbsp; 11&nbsp;#  Frank Wang 建议用下边的方法: 12&nbsp;   rnumber=$(( RANDOM%27/3*3+6 ))</PRE></FONT></TD></TR></TABLE>	</P><P>	<EM>Bill Gradwohl</EM>给出了一个改良公式, 这个公式只适用于正书.	<TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))</PRE></FONT></TD></TR></TABLE>	</P><P>这里Bill展示了一个通用公式, 	  这个函数返回两个指定值之间的随机数. </P><DIVCLASS="EXAMPLE"><HR><ANAME="RANDOMBETWEEN"></A><P><B>例子 9-27. 两个指定值之间的随机数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# random-between.sh  3&nbsp;# 产生两个指定值之间的随机数.  4&nbsp;# 由Bill Gradwohl编写, 本书作者做了一些修改.  5&nbsp;# 脚本作者允许在这里使用.  6&nbsp;  7&nbsp;  8&nbsp;randomBetween() {  9&nbsp;   #  在$min和$max之间, 10&nbsp;   #+ 产生一个正的或负的随机数. 11&nbsp;   #+ 并且可以被$divisibleBy所整除. 12&nbsp;   #  给出一个合理的随机分配的返回值. 13&nbsp;   # 14&nbsp;   #  Bill Gradwohl - Oct 1, 2003 15&nbsp; 16&nbsp;   syntax() { 17&nbsp;   # 在函数中内嵌函数 18&nbsp;      echo 19&nbsp;      echo    "Syntax: randomBetween [min] [max] [multiple]" 20&nbsp;      echo 21&nbsp;      echo    "Expects up to 3 passed parameters, but all are completely optional." 22&nbsp;      echo    "min is the minimum value" 23&nbsp;      echo    "max is the maximum value" 24&nbsp;      echo    "multiple specifies that the answer must be a multiple of this value." 25&nbsp;      echo    "    i.e. answer must be evenly divisible by this number." 26&nbsp;      echo     27&nbsp;      echo    "If any value is missing, defaults area supplied as: 0 32767 1" 28&nbsp;      echo    "Successful completion returns 0, unsuccessful completion returns" 29&nbsp;      echo    "function syntax and 1." 30&nbsp;      echo    "The answer is returned in the global variable randomBetweenAnswer" 31&nbsp;      echo    "Negative values for any passed parameter are handled correctly." 32&nbsp;   } 33&nbsp; 34&nbsp;   local min=${1:-0} 35&nbsp;   local max=${2:-32767} 36&nbsp;   local divisibleBy=${3:-1} 37&nbsp;   # 默认值分配, 用来处理没有参数传递进来的情况. 38&nbsp; 39&nbsp;   local x 40&nbsp;   local spread 41&nbsp; 42&nbsp;   # 确认divisibleBy是正值. 43&nbsp;   [ ${divisibleBy} -lt 0 ] &#38;&#38; divisibleBy=$((0-divisibleBy)) 44&nbsp; 45&nbsp;   # 完整性检查. 46&nbsp;   if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o  ${min} -eq ${max} ]; then  47&nbsp;      syntax 48&nbsp;      return 1 49&nbsp;   fi 50&nbsp; 51&nbsp;   # 查看min和max是否颠倒了. 52&nbsp;   if [ ${min} -gt ${max} ]; then 53&nbsp;      # 交换它们. 54&nbsp;      x=${min} 55&nbsp;      min=${max} 56&nbsp;      max=${x} 57&nbsp;   fi 58&nbsp; 59&nbsp;   #  如果min自己并不能够被$divisibleBy所整除, 60&nbsp;   #+ 那么就调整max的值, 使其能够被$divisibleBy所整除, 前提是不能放大范围. 61&nbsp;   if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then  62&nbsp;      if [ ${min} -lt 0 ]; then 63&nbsp;         min=$((min/divisibleBy*divisibleBy)) 64&nbsp;      else 65&nbsp;         min=$((((min/divisibleBy)+1)*divisibleBy)) 66&nbsp;      fi 67&nbsp;   fi 68&nbsp; 69&nbsp;   #  如果min自己并不能够被$divisibleBy所整除, 70&nbsp;   #+ 那么就调整max的值, 使其能够被$divisibleBy所整除, 前提是不能放大范围. 71&nbsp;   if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then  72&nbsp;      if [ ${max} -lt 0 ]; then 73&nbsp;         max=$((((max/divisibleBy)-1)*divisibleBy)) 74&nbsp;      else 75&nbsp;         max=$((max/divisibleBy*divisibleBy)) 76&nbsp;      fi 77&nbsp;   fi 78&nbsp; 79&nbsp;   #  --------------------------------------------------------------------- 80&nbsp;   #  现在, 来做点真正的工作. 81&nbsp; 82&nbsp;   #  注意, 为了得到对于端点来说合适的分配, 83&nbsp;   #+ 随机值的范围不得不落在 84&nbsp;   #+ 0 和 abs(max-min)+divisibleBy 之间, 而不是 abs(max-min)+1. 85&nbsp; 86&nbsp;   #  对于端点来说, 87&nbsp;   #+ 这个少量的增加将会产生合适的分配. 88&nbsp; 89&nbsp;   #  如果修改这个公式, 使用 abs(max-min)+1 来代替 abs(max-min)+divisibleBy的话,  90&nbsp;   #+ 也能够得到正确的答案, 但是在这种情况下所生成的随机值对于正好为端点倍数 91&nbsp;   #+ 的这种情况来说将是不完美的, 因为正好为端点倍数情况下的随机率比较低, 92&nbsp;   #+ 因为你才加1而已, 这比正常的公式下所产生的几率要小的多(正常为加divisibleBy). 93&nbsp;   #  --------------------------------------------------------------------- 94&nbsp; 95&nbsp;   spread=$((max-min)) 96&nbsp;   [ ${spread} -lt 0 ] &#38;&#38; spread=$((0-spread)) 97&nbsp;   let spread+=divisibleBy 98&nbsp;   randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min))    99&nbsp;100&nbsp;   return 0101&nbsp;102&nbsp;   #  然而, Paulo Marcel Coelho Aragao 指出103&nbsp;   #+ 当 $max 和 $min 不能够被$divisibleBy所整除时,104&nbsp;   #+ 这个公式将会失败.105&nbsp;   #106&nbsp;   #  他建议使用如下公式:107&nbsp;   #    rnumber = $(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy))108&nbsp;109&nbsp;}110&nbsp;111&nbsp;# 让我们测试一下这个函数.112&nbsp;min=-14113&nbsp;max=20114&nbsp;divisibleBy=3115&nbsp;116&nbsp;117&nbsp;#  产生一个所期望的数组answers, 数组下标用来表示在范围内可能出现的值,118&nbsp;#+ 而元素内容记录的是这个值所出现的次数, 如果我们循环足够多次, 那么我们一定会得到至少一次出现机会.119&nbsp;120&nbsp;declare -a answer121&nbsp;minimum=${min}122&nbsp;maximum=${max}123&nbsp;   if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then 124&nbsp;      if [ ${minimum} -lt 0 ]; then125&nbsp;         minimum=$((minimum/divisibleBy*divisibleBy))126&nbsp;      else127&nbsp;         minimum=$((((minimum/divisibleBy)+1)*divisibleBy))128&nbsp;      fi129&nbsp;   fi130&nbsp;131&nbsp;132&nbsp;132    #  如果max本身并不能够被$divisibleBy整除,133&nbsp;133    #+ 那么就调整max的值, 使其能够被$divisibleBy整除, 前提是不能放大范围.134&nbsp;135&nbsp;   if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then 136&nbsp;      if [ ${maximum} -lt 0 ]; then137&nbsp;         maximum=$((((maximum/divisibleBy)-1)*divisibleBy))138&nbsp;      else139&nbsp;         maximum=$((maximum/divisibleBy*divisibleBy))140&nbsp;      fi141&nbsp;   fi142&nbsp;143&nbsp;144&nbsp;#  我们需要产生一个下标全部为正的数组.145&nbsp;#+ 所以我们需要一个displacement, 146&nbsp;#+ 这样就可以保证结果都为正. 147&nbsp;148&nbsp;displacement=$((0-minimum))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?