Skip to content

Latest commit

 

History

History
969 lines (786 loc) · 32 KB

ch9-3.md

File metadata and controls

969 lines (786 loc) · 32 KB

9.3. $RANDOM: generate random integer

9.3. $RAMDOM: 產生隨機變數


Anyone who attempts to generate random numbers by deterministic means is, of course, living in a state of sin.

任何想用 deterministic 方法產生隨機數字者,都活在 sin 的狀態。

--John von Neumann

$RANDOM is an internal Bash function (not a constant) that returns a pseudorandom [1] integer in the range 0 - 32767.

$RANDOM 是 BAsh 內建函式(非常數),其回傳一範圍從 0 至 32767 的偽亂數[1]。

It should not be used to generate an encryption key.

不應用來產生加密金鑰。

Example 9-11. Generating random numbers

範例 9-11. 產生隨機數字

#!/bin/bash

# $RANDOM returns a different random integer at each invocation.
# $RANDOM 每次觸發後,回傳一隨機整數值。
# Nominal range: 0 - 32767 (signed 16-bit integer).
# 標稱範圍: 0 - 32767 (signed 16-bit 整數)

MAXCOUNT=10
count=1

echo
echo "$MAXCOUNT random numbers:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ]      # Generate 10 ($MAXCOUNT) random integers.
                                      # 產生 10 個隨機整數。
do
  number=$RANDOM
  echo $number
  let "count += 1"  # Increment count.
                    # 增加 count 數量。
done
echo "-----------------"

# If you need a random int within a certain range, use the 'modulo' operator.
# 若需產生一特定範圍內之整數亂數,用 modulo 運算元。
# 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.
# 若想有個比 lower bound 還大的隨機整數,設定一測試,以過濾不符合值。

FLOOR=200

number=0   #initialize
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
done
echo "Random number greater than $FLOOR ---  $number"
# 比 $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.
   # 如此可中止 while 迴圈,並跑快一點。
   # But, there might be a problem with that. What is it?
   # 但這樣做可能有問題。啥問題?



# Combine above two techniques to retrieve random number between two limits.
# 結合上面兩種方法以取得兩 limits 間隨機數字。
number=0   #initialize
           # 初始化
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
  let "number %= $RANGE"  # Scales $number down within $RANGE.
                          # 在範圍內縮小 $number。
done
echo "Random number between $FLOOR and $RANGE ---  $number"
    # 在 $FLOOR 與 $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).
# 注意, let "number >>= 14" 可以得到更好的隨機分配 (除了最後一個 binary digit)外其餘 digit 都會 right shift out。 
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.
          # 除數 6,可將範圍限制在 0-5。
          # Incrementing by 1 gives desired range of 1 - 6.
          # 每次遞增 1,從 1-6。
          # Thanks, Paulo Marcel Coelho Aragao, for the simplification.
          # 感謝 Paulo Marcel Coelho Aragao 提供此簡化方法。
die1=0
die2=0
# Would it be better to just set SPOTS=7 and not add 1? Why or why not?
# 若直接設 SPOTS=7 會較好嗎?為什麼?

# Tosses each die separately, and so gives correct odds.
# 獨立擲每個骰子,可得到 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"
     # 總點數為 = $throw
echo


exit 0

Example 9-12. Picking a random card from a deck

範例 9-12。從桌上隨機選一卡片。

#!/bin/bash
# pick-card.sh

# This is an example of choosing random elements of an array.
# 此範例從陣列中隨機挑選一 elements。

# 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.
                               # 計算 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.
# 感謝 jipe 提醒 $RANDOM 還能有此用法。
exit 0

Example 9-13. Brownian Motion Simulation

範例 9-13 模擬布朗運動。

#!/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.
#  它也可被視為是一次 Galton Board 的剝離模擬,
#  也可視為釘了一個 pattern of pags 的傾斜板子,
#  向下一次滾動一個大理石。
#+ 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.
#  想像成是 bare-bones Pachinko 遊戲。
#  As you see by running the script,
#  腳本運行時,我們可看見
#+ most of the marbles cluster around the center slot.
#  大多數大理石圍繞著中心點聚集。
#+ This is consistent with the expected binomial distribution.
#  這與 binomiial 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.
#  在 Galton Board 模擬中,腳本忽略如 board tilt-angle, rolling friction of the marbles, angles of impact 以及 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.
                      #  $RANDOM 的範圍是 0 - 2。 
POS=0                 #  Left/right position.
                      #  左/右方位。
RANDOM=$$             #  Seeds the random number generator from PID
                      #+ of script.
                      #  亂數種子是該腳本的 PID。
                      
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.
                      # 將陣列內所有 elements 都歸零。
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.
     #  注意,若超過 99 個插槽則顯示出來會是一團亂。
     #  Running only(!) 500 passes usually avoids this.
     #  只跑 500 passes 通常可避免此問題。
  }

                       
Move () {              # Move one unit right / left, or stay put.
                       # 左右移動或 stay put
  Move=$RANDOM         # How random is $RANDOM? Well, let's see ...
                       # $RAMDOM 值如何是隨機的?我們來瞧瞧。
  let "Move %= RANGE"  # Normalize into range of 0 - 2.
                       # 正規化範圍 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).
                             # 單次 pass (內部迴圈)。
i=0
while [ "$i" -lt "$ROWS" ]   # One event per row.
                             # 每個欄表示一個事件。
do
  Move
  ((i++));
done

SHIFT=11                     # Why 11, and not 10?
                             # 為何是 11 而非 10?
let "POS += $SHIFT"          # Shift "zero position" to center.
                             # 將 zero position 移到中央。
(( Slots[$POS]++ ))          # DEBUG: echo $POS
                             # 除錯方法: 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.
#  1) 將結果以垂直長條圖顯示,或以散點圖顯示。
#  2) Alter the script to use /dev/urandom instead of $RANDOM.
#  2) 將腳本內 $RANDOM 改用 /dev/urandom。 
#     Will this make the results more random?
#     會讓隨機更「隨機」嗎?
#  3) Provide some sort of "animation" or graphic output
#     for each marble played.
#  3) 每一輪模擬提供一些類似動畫或圖像。

Jipe points out a set of techniques for generating random numbers within a range.

Jipe 提出幾個可在一定範圍內產生隨機數的方法。

#  Generate random number between 6 and 30.
#  產生 6 至 30 之間的隨機數。
   rnumber=$((RANDOM%25+6))	

#  Generate random number in the same 6 - 30 range,
#  同樣產生 6 至 30 之間的隨機數,
#+ but the number must be evenly divisible by 3.
#  但此隨機數會被 3 均勻除。
   rnumber=$(((RANDOM%30/3+1)*3))

#  Note that this will not work all the time.
#  注意,這不見得每次都會成功。
#  It fails if $RANDOM%30 returns 0.
#  若 $RANDOM%30 回傳 0,就會失敗。

#  Frank Wang suggests the following alternative:
#  Frank Wang 建議用下面的方法:
   rnumber=$(( RANDOM%27/3*3+6 ))

Bill Gradwohl came up with an improved formula that works for positive numbers.

Bill Gradwohl 提出一個更好的方程式,讓 positive numbers 也適用。

rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))

Here Bill presents a versatile function that returns a random number between two specified values.

這裡 Bill 提出一個很有才的 function,會回傳一介於兩 special values 間的隨機數。

Example 9-14. Random between values

範例 9-14. 在兩值之間取隨機。

#!/bin/bash
# random-between.sh
# Random number between two specified values. 
# 在特定兩值之間取隨機。
# Script by Bill Gradwohl, with minor modifications by the document author.
# 腳本由 Bill Gradwohl 提供本文件作者僅做些微修改。
# Corrections in lines 187 and 189 by Anthony Le Clezio. 
# 感謝 Anthony Le Clezio 修正 187 及 189 行。
# Used with permission.
# 使用時請注意權限。

randomBetween() {
   #  Generates a positive or negative random number
   #+ between $min and $max
   #+ and divisible by $divisibleBy.
   #  產生一個正或負的隨機數,範圍介於 $min 及 $max,且是可被 $divisibleBy 除的。
   #  Gives a "reasonably random" distribution of return values.
   #  讓回傳值符合 reasonably random distribution。
   #
   #  Bill Gradwohl - Oct 1, 2003

   syntax() {
   # Function embedded within function.
   # Function 內嵌 function。
      echo
      echo    "Syntax: randomBetween [min] [max] [multiple]"
              # 用法: randomBetween [min] [max] [multiple]
      echo
      echo -n "Expects up to 3 passed parameters, "
              # 預期有三個參數。
      echo    "but all are completely optional."
              # 但全都是完全 optional。
      echo    "min is the minimum value"
              # min 就是最小值。
      echo    "max is the maximum value"
              # max 就是最大值。
      echo -n "multiple specifies that the answer must be "
      echo     "a multiple of this value."
              #"multiple 即是 answer 必須為該值的倍數。"
      echo    "    i.e. answer must be evenly divisible by this number."
              # 例如 answer 要能被此數整除。
      echo    
      echo    "If any value is missing, defaults area supplied as: 0 32767 1"
              # 若有任何值缺了,預設值的範圍落在 0 32767 1 
      echo -n "Successful completion returns 0, "
              # 若執行成功,回傳零值。
      echo    "unsuccessful completion returns"
      echo    "function syntax and 1."
              # 不成功者回傳 function syntax 及 1 。
      echo -n "The answer is returned in the global variable "
      echo    "randomBetweenAnswer"
              # 答案將會以 randomBetweenAnswer 為名的 global variable 回傳。
      echo -n "Negative values for any passed parameter are "
      echo    "handled correctly."
              # passed parameter 若接收到 negative values 時,這些負值將會被正確的處理。
   }

   local min=${1:-0}
   local max=${2:-32767}
   local divisibleBy=${3:-1}
   # Default values assigned, in case parameters not passed to function.
   # 為防 function 沒接到參數值,設定預設值。
   
   local x
   local spread

   # Let's make sure the divisibleBy value is positive.
   # 我們先確認 divisibleBy value 是正值。
   [ ${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.
   # 檢查 min 與 max 是否相反。
   if [ ${min} -gt ${max} ]; then
      # Swap them.
      x=${min}
      min=${max}
      max=${x}
   fi

   #  If min is itself not evenly divisible by $divisibleBy,
   #  若 min 是自身且無法被 $divisibleBy 整除,
   #+ then fix the min to be within range.# 
   #  則處理 min 使其介於範圍內。
   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,
   #  若 max 是自身且無法被 $divisibleBy 整除,
   #+ then fix the max to be within range.
   #  則處理 max 使其介於範圍內。
   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,
   #  請注意,若 end points 要得到 proper distribution,
   #+ the range of random values has to be allowed to go between
   #+ 0 and abs(max-min)+divisibleBy, not just abs(max-min)+1.
   #  隨機值範圍需介於 0 與 abs(max-min)+divisibleBy 而非 abs(max-min)+1。
  
   #  The slight increase will produce the proper distribution for the
   #+ end points.
   #  逐步微增加,可讓 end points 遵照適當的分配。
  
   #  Changing the formula to use abs(max-min)+1 will still produce
   #+ correct answers,
   #  改用 abs(max-min)+1 還是可產生正確的答案,
   #  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.
   #  因為有幾次 end points ($min 及 $max) 的回傳值都低於 correct formula 使用時回傳的值。
   #  ---------------------------------------------------------------------

   spread=$((max-min))
   #  Omair Eshkenazi points out that this test is unnecessary,
   #  Omair Eshkenazi 指出此測試是不必要的,
   #+ since max and min have already been switched around.
   #  因為 max 跟 min 已彼此交換了。
   [ ${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
   #  然而,Paulo Marcel Coelho Aragao 指出
   #+ when $max and $min are not divisible by $divisibleBy,
   #  當 $max 與 $min 無法被 $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,
   #  若 max 本身無法被 $divisibleBy 整除,
   #+ then fix the max to be within range.
   #  則修正 max,使其值在範圍中。

   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,
#  我們須只產生 positive array subscripts,
#+ so we need a displacement that that will guarantee
#+ positive results.
#  所以我們必須置換值以確保 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,
              #  腳本作者建議跑 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.
   #  注意,我們 min 與 max 以相反方向 specifying,使其在此案例中之 function correct。

   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

Just how random is $RANDOM?

$RANDOM 有多隨機呢?

The best way to test this is to write a script that tracks the distribution of "random" numbers generated by $RANDOM.

測試隨機度的最佳方法就是撰寫一腳本,追蹤 $RANDOM 產出的隨機數字是趨近於何種分配。

Let's roll a $RANDOM die a few times . . .

咱們來骰 $RANDOM 骰子吧...

Example 9-15. Rolling a single die with RANDOM

範例 9-15. 隨機擲骰

#!/bin/bash
# How random is RANDOM?
# 到底 RANDOM 能有多隨機呢?

RANDOM=$$       # Reseed the random number generator using script process ID.
                # 用 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.
                #  因為未初始化變數的值為 null,不是零。
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.
#  假設 RANDOM 是真隨機的話,分數應被平均分佈於各處。
#  With $MAXTHROWS at 600, all should cluster around 100,
#  若 $MAXTHROWS 設為 600,則各種值出現數大概會接近 100 次,
#+ plus-or-minus 20 or so.
#  正負二十左右。
#
#  Keep in mind that RANDOM is a ***pseudorandom*** generator,
#  請記住,RANDOM 是一個 pseudorandom 產生器,
#+ 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.
#  Sufficiently long "random" sequences 可能會導致混亂且其他非隨機的行為。

# Exercise (easy):
# 練習(簡單):
# ---------------
# Rewrite this script to flip a coin 1000 times.
# 重寫此腳本,將硬幣投擲一千次。
# Choices are "HEADS" and "TAILS."
# 結果唯二,正反兩面。

As we have seen in the last example,

從上一範例中我們可見,

it is best to reseed the RANDOM generator each time it is invoked.

每次產生亂數時,建議都要 reseed。

Using the same seed for RANDOM repeats the same series of numbers.

若使用同的 seed,則每次的結果都會是那一批。

[2] (This mirrors the behavior of the random() function in C.)

[2] 這其實與 C 語言中的 random() 函式是一樣的。

Example 9-16. Reseeding RANDOM

範例 9-16. 重選亂數種子

#!/bin/bash
# seeding-random.sh: Seeding the RANDOM variable.
# 播種 RANDOM 變數。
# 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.
                  # 設定 seeds。
echo "Random seed = $SEED"
random_numbers


RANDOM=$SEED      # Same seed for RANDOM . . .
                  # 同一個種子。
echo; echo "Again, with same random seed ..."
           # 再一次使用相同的 random seed ...
echo "Random seed = $SEED"
random_numbers    # . . . reproduces the exact same number series.
                  # . .  . 產出同一串數字。
                  #
                  # When is it useful to duplicate a "random" series?
                  # 何時才是複製 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.
# 用 process id 為 RANDOM 種子。
# It is also possible to seed RANDOM from 'time' or 'date' commands.
# 也可用 time 或 date 指令當作 RANDOM 的種子。

# Getting fancy...
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }'| sed s/^0*//)
#  Pseudo-random output fetched
#  接收到 Pseudi-random 之結果
#+ from /dev/urandom (system pseudo-random device-file),
#  從 /dev/urandom (system pseudo-random device-file) 接收,
#+ then converted to line of printable (octal) numbers by "od",
#  接著用 od 將之轉成一行 printable (octal) numbers,
#+ then "awk" retrieves just one number for SEED,
#  用 awk 接收 SEED,
#+ finally "sed" removes any leading zeros.
#  最後用 sed 將 leading zeros 給移除。
RANDOM=$SEED
echo "Random seed = $SEED"
random_numbers

echo; echo

exit 0

The /dev/urandom pseudo-device file provides a method

/dev/urandom pseudo-device file 提供了一個方法 of generating much more "random" pseudorandom numbers than the $RANDOM variable. 此方法與 $RANDOM 比起來,可產生更多的 random pseudorandom numbers。 dd if=/dev/urandom of=targetfile bs=1 count=XX creates a file of well-scattered pseudorandom numbers. dd if=/dev/urandom of=targetfile bs=1 count=XX 會建立一個 well-scattered pseudorandom numbers 的檔案。

However, assigning these numbers to a variable in a script requires a workaround,

然而,將這些數字指派給腳本中的變數需要用 workaround 的方式, such as filtering through od (as in above example, Example 16-14, and Example A-36), 例如透過 od 來過濾 (如範例 16-14 以及範例 A-36)

or even piping to md5sum (see Example 36-16).

或甚至 piping 給 md5sum (詳見範例 36-16)。

There are also other ways to generate pseudorandom numbers in a script.

也有其他方式可在腳本中產生 pseudorandom numbers。

Awk provides a convenient means of doing this.

Awk 提供了一簡易方法來做。

Example 9-17. Pseudorandom numbers, using awk

範例 9-17. 用 awk 產生 Pseudorandom numbers。

#!/bin/bash
#  random2.sh: Returns a pseudorandom number in the range 0 - 1,
#  random2.sh: 回傳一個介於 0 - 1 的 pseudorandom number 至 6 個 decimal places。
#+ to 6 decimal places. For example: 0.822725
#  例如:0.822725
#  Uses the awk rand() function.
#  使用 awk rand() 函式。

AWKSCRIPT=' { srand(); print rand() } '
#           Command(s)/parameters passed to awk
# Note that srand() reseeds awk's random number generator.
# 請注意, srand() 會 reseeds awk 的 random number generator。


echo -n "Random number between 0 and 1 = "
# 在 0 與 1 間的隨機數。

echo | awk "$AWKSCRIPT"
# What happens if you leave out the 'echo'?
# 若不用 'echo' 結果會如何?

exit 0


# Exercises:
# 練習:
# ---------

# 1) Using a loop construct, print out 10 different random numbers.
# 1) 使用 loop 印出 10 個不同的隨機數。
#      (Hint: you must reseed the srand() function with a different seed
#+     in each pass through the loop. What happens if you omit this?)
#      (提示: 必須在 each pass 中 reseed srand()。 

# 2) Using an integer multiplier as a scaling factor, generate random numbers 
#+   in the range of 10 to 100.
# 2) 用 integer multiplier 為 scaling factor,產生 10 ~ 100 範圍內的隨機數。

# 3) Same as exercise #2, above, but generate random integers this time.
# 3) 與練習 #2 相同, 但這次產生的會是隨機整數。

The date command also lends itself to generating pseudorandom integer sequences.

date 指令也可將其結果予以產出 pseudorandom integer sequences。

Notes

筆記

[1] True "randomness," insofar as it exists at all,

[1] 真正的「隨機」,目前為止只存在於,

can only be found in certain incompletely understood natural phenomena,

只能在特定 incompletely understood natural phenomena 中被發現,

such as radioactive decay.

例如放射線衰退。

Computers only simulate randomness,

電腦只模擬 randomness,

and computer-generated sequences of "random" numbers are therefore referred to as pseudorandom.

computer-generated sequences of "random" numbers 就被 referred 成 pseudorandom。

[2] The seed of a computer-generated pseudorandom number series can be considered an identification label.

[2] comouter-generated pseudorandom number series 的種子可被視為 identification label。

For example, think of the pseudorandom series with a seed of 23 as Series #23.

例如,試著以 23 為種子的 pseudorandom series as Series #23。

A property of a pseurandom number series is the length of the cycle before it starts repeating itself.

pseurandom number series 的其中一個特質就是 cycle 在 starts repeating itself 前的長度。

A good pseurandom generator will produce series with very long cycles.

一個好的 pseurandom 產生器會產出 series with very long cycles。

url: http://tldp.org/LDP/abs/html/randomvar.html 參考連結:http://tldp.org/LDP/abs/html/randomvar.html