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 #!/bin/bash 2 3 # 每次调用$RANDOM都会返回不同的随机整数. 4 # 一般范围为: 0 - 32767 (有符号的16-bit整数). 5 6 MAXCOUNT=10 7 count=1 8 9 echo 10 echo "$MAXCOUNT random numbers:" 11 echo "-----------------" 12 while [ "$count" -le $MAXCOUNT ] # 产生10 ($MAXCOUNT)个随机整数. 13 do 14 number=$RANDOM 15 echo $number 16 let "count += 1" # 增加计数. 17 done 18 echo "-----------------" 19 20 # 如果你需要在特定范围内产生随机整数, 那么使用'modulo'(模)操作.(译者注: 事实上, 这不是一个非常好的办法. 理由见man 3 rand) 21 # 取模操作会返回除法的余数. 22 23 RANGE=500 24 25 echo 26 27 number=$RANDOM 28 let "number %= $RANGE" 29 # ^^ 30 echo "Random number less than $RANGE --- $number" 31 32 echo 33 34 35 36 # 如果你需要产生一个大于某个下限的随机整数. 37 #+ 那么建立一个test循环来丢弃所有小于此下限值的整数. 38 39 FLOOR=200 40 41 number=0 #初始化 42 while [ "$number" -le $FLOOR ] 43 do 44 number=$RANDOM 45 done 46 echo "Random number greater than $FLOOR --- $number" 47 echo 48 49 # 让我们对上边的循环尝试一个小改动, 如下: 50 # let "number = $RANDOM + $FLOOR" 51 # 这将不再需要那个while循环, 并且能够运行的更快. 52 # 但是, 这可能会产生一个问题, 思考一下是什么问题? 53 54 55 56 # 结合上边两个例子, 来在指定的上下限之间来产生随机数. 57 number=0 #initialize 58 while [ "$number" -le $FLOOR ] 59 do 60 number=$RANDOM 61 let "number %= $RANGE" # 让$number依比例落在$RANGE的范围内. 62 done 63 echo "Random number between $FLOOR and $RANGE --- $number" 64 echo 65 66 67 68 # 产生二元值, 就是, "true" 或 "false" 两个值. 69 BINARY=2 70 T=1 71 number=$RANDOM 72 73 let "number %= $BINARY" 74 # 注意 let "number >>= 14" 将会给出一个更好的随机分配. #(译者注: 正如man页中提到的, 更高位的随机分布更加平均) 75 #+ (右移14位将把所有的位全部清空, 除了第15位, 因为有符号, 第16位是符号位). #取模操作使用低位来产生随机数会相对不平均) 76 if [ "$number" -eq $T ] 77 then 78 echo "TRUE" 79 else 80 echo "FALSE" 81 fi 82 83 echo 84 85 86 # 抛骰子. 87 SPOTS=6 # 模6给出的范围是0 - 5. 88 # 加1会得到期望的范围1 - 6. 89 # 感谢, Paulo Marcel Coelho Aragao, 对此进行的简化. 90 die1=0 91 die2=0 92 # 是否让SPOTS=7会比加1更好呢? 解释行或者不行的原因? 93 94 # 每次抛骰子, 都会给出均等的机会. 95 96 let "die1 = $RANDOM % $SPOTS +1" # 抛第一次. 97 let "die2 = $RANDOM % $SPOTS +1" # 抛第二次. 98 # 上边的算术操作中, 哪个具有更高的优先级呢 -- 99 #+ 模(%) 还是加法操作(+)?100 101 102 let "throw = $die1 + $die2"103 echo "Throw of the dice = $throw"104 echo105 106 107 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 #!/bin/bash 2 # pick-card.sh 3 4 # 这是一个从数组中取出随机元素的一个例子. 5 6 7 # 抽取一张牌, 任何一张. 8 9 Suites="Clubs 10 Diamonds 11 Hearts 12 Spades" 13 14 Denominations="2 15 3 16 4 17 5 18 6 19 7 20 8 21 9 22 10 23 Jack 24 Queen 25 King 26 Ace" 27 28 # 注意变量的多行展开. 29 30 31 suite=($Suites) # 读入一个数组. 32 denomination=($Denominations) 33 34 num_suites=${#suite[*]} # 计算有多少个数组元素. 35 num_denominations=${#denomination[*]} 36 37 echo -n "${denomination[$((RANDOM%num_denominations))]} of " 38 echo ${suite[$((RANDOM%num_suites))]} 39 40 41 # $bozo sh pick-cards.sh 42 # Jack of Clubs 43 44 45 # 感谢, "jipe," 指出$RANDOM的这个用法. 46 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 # 在6 到 30之间产生随机数. 2 rnumber=$((RANDOM%25+6)) 3 4 # 还是在6 - 30之间产生随机数, 5 #+ 但是这个数还必须能够被3整除. 6 rnumber=$(((RANDOM%30/3+1)*3)) 7 8 # 注意, 并不是在所有情况下都能正确运行. 9 # 如果$RANDOM返回0, 那么就会失败. 10 11 # Frank Wang 建议用下边的方法: 12 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 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 #!/bin/bash 2 # random-between.sh 3 # 产生两个指定值之间的随机数. 4 # 由Bill Gradwohl编写, 本书作者做了一些修改. 5 # 脚本作者允许在这里使用. 6 7 8 randomBetween() { 9 # 在$min和$max之间, 10 #+ 产生一个正的或负的随机数. 11 #+ 并且可以被$divisibleBy所整除. 12 # 给出一个合理的随机分配的返回值. 13 # 14 # Bill Gradwohl - Oct 1, 2003 15 16 syntax() { 17 # 在函数中内嵌函数 18 echo 19 echo "Syntax: randomBetween [min] [max] [multiple]" 20 echo 21 echo "Expects up to 3 passed parameters, but all are completely optional." 22 echo "min is the minimum value" 23 echo "max is the maximum value" 24 echo "multiple specifies that the answer must be a multiple of this value." 25 echo " i.e. answer must be evenly divisible by this number." 26 echo 27 echo "If any value is missing, defaults area supplied as: 0 32767 1" 28 echo "Successful completion returns 0, unsuccessful completion returns" 29 echo "function syntax and 1." 30 echo "The answer is returned in the global variable randomBetweenAnswer" 31 echo "Negative values for any passed parameter are handled correctly." 32 } 33 34 local min=${1:-0} 35 local max=${2:-32767} 36 local divisibleBy=${3:-1} 37 # 默认值分配, 用来处理没有参数传递进来的情况. 38 39 local x 40 local spread 41 42 # 确认divisibleBy是正值. 43 [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy)) 44 45 # 完整性检查. 46 if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o ${min} -eq ${max} ]; then 47 syntax 48 return 1 49 fi 50 51 # 查看min和max是否颠倒了. 52 if [ ${min} -gt ${max} ]; then 53 # 交换它们. 54 x=${min} 55 min=${max} 56 max=${x} 57 fi 58 59 # 如果min自己并不能够被$divisibleBy所整除, 60 #+ 那么就调整max的值, 使其能够被$divisibleBy所整除, 前提是不能放大范围. 61 if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then 62 if [ ${min} -lt 0 ]; then 63 min=$((min/divisibleBy*divisibleBy)) 64 else 65 min=$((((min/divisibleBy)+1)*divisibleBy)) 66 fi 67 fi 68 69 # 如果min自己并不能够被$divisibleBy所整除, 70 #+ 那么就调整max的值, 使其能够被$divisibleBy所整除, 前提是不能放大范围. 71 if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then 72 if [ ${max} -lt 0 ]; then 73 max=$((((max/divisibleBy)-1)*divisibleBy)) 74 else 75 max=$((max/divisibleBy*divisibleBy)) 76 fi 77 fi 78 79 # --------------------------------------------------------------------- 80 # 现在, 来做点真正的工作. 81 82 # 注意, 为了得到对于端点来说合适的分配, 83 #+ 随机值的范围不得不落在 84 #+ 0 和 abs(max-min)+divisibleBy 之间, 而不是 abs(max-min)+1. 85 86 # 对于端点来说, 87 #+ 这个少量的增加将会产生合适的分配. 88 89 # 如果修改这个公式, 使用 abs(max-min)+1 来代替 abs(max-min)+divisibleBy的话, 90 #+ 也能够得到正确的答案, 但是在这种情况下所生成的随机值对于正好为端点倍数 91 #+ 的这种情况来说将是不完美的, 因为正好为端点倍数情况下的随机率比较低, 92 #+ 因为你才加1而已, 这比正常的公式下所产生的几率要小的多(正常为加divisibleBy). 93 # --------------------------------------------------------------------- 94 95 spread=$((max-min)) 96 [ ${spread} -lt 0 ] && spread=$((0-spread)) 97 let spread+=divisibleBy 98 randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min)) 99 100 return 0101 102 # 然而, Paulo Marcel Coelho Aragao 指出103 #+ 当 $max 和 $min 不能够被$divisibleBy所整除时,104 #+ 这个公式将会失败.105 #106 # 他建议使用如下公式:107 # rnumber = $(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy))108 109 }110 111 # 让我们测试一下这个函数.112 min=-14113 max=20114 divisibleBy=3115 116 117 # 产生一个所期望的数组answers, 数组下标用来表示在范围内可能出现的值,118 #+ 而元素内容记录的是这个值所出现的次数, 如果我们循环足够多次, 那么我们一定会得到至少一次出现机会.119 120 declare -a answer121 minimum=${min}122 maximum=${max}123 if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then 124 if [ ${minimum} -lt 0 ]; then125 minimum=$((minimum/divisibleBy*divisibleBy))126 else127 minimum=$((((minimum/divisibleBy)+1)*divisibleBy))128 fi129 fi130 131 132 132 # 如果max本身并不能够被$divisibleBy整除,133 133 #+ 那么就调整max的值, 使其能够被$divisibleBy整除, 前提是不能放大范围.134 135 if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then 136 if [ ${maximum} -lt 0 ]; then137 maximum=$((((maximum/divisibleBy)-1)*divisibleBy))138 else139 maximum=$((maximum/divisibleBy*divisibleBy))140 fi141 fi142 143 144 # 我们需要产生一个下标全部为正的数组.145 #+ 所以我们需要一个displacement, 146 #+ 这样就可以保证结果都为正. 147 148 displacement=$((0-minimum))
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?