هر شخصی که تلاش میکند اعداد تصادفی را با روشهای قطعی تولید کند، به طور حتم در اشتباه به سر میبرد. --John von Neumann |
$RANDOM یک تابع درونی Bash است (نه یک ثابت) که یک عدد صحیح تصادفی ساختگی [1] در محدوده 0 - 32767 تولید میکند. نباید برای تولید یک کلید رمزنگاری به کار برود.
مثال 9-11. تولید اعداد تصادفی
#!/bin/bash #$RANDOM در هر نوبت یک عدد صحیح تصادفی متفاوتی برگشت میدهد. #محدوده اسمی آن: 0 - 32767 (صحیح علامتدار شانزده بیتی) است. MAXCOUNT=10 count=1 echo echo "$MAXCOUNT random numbers:" echo "-----------------" while [ "$count" -le $MAXCOUNT ] #تولید 10 ($MAXCOUNT) عدد صحیح تصادفی. do number=$RANDOM echo $number let "count += 1" # done echo "-----------------" #اگر به عدد صحیح تصادفی در یک محدوده معین نیاز دارید، عملگر modulo را # RANGE=500 echo number=$RANDOM let "number %= $RANGE" # echo "Random number less than $RANGE --- $number" echo # #+ FLOOR=200 number=0 # while [ "$number" -le $FLOOR ] do number=$RANDOM done echo "Random number greater than $FLOOR --- $number" echo # #کنیم، یعنی let "number = $RANDOM + $FLOOR" را که # # # number=0 # while [ "$number" -le $FLOOR ] do number=$RANDOM let "number %= $RANGE" #کاهش $number به کمتر از $RANGE done echo "Random number between $FLOOR and $RANGE --- $number" echo #تولید گزینه دوحالته، یعنی کمیت true یا false. BINARY=2 T=1 number=$RANDOM let "number %= $BINARY" #توجه کنید که let "number >>= 14" پراکندگی تصادفی بهتری ارایه میکند. #+ #if [ "$number" -eq $T ] then echo "TRUE" else echo "FALSE" fi echo # SPOTS=6 # مترجم: در اصل number مساوی آخرین بیت سمت چپ عدد تصادفی تولیدی میشود. باقیمانده تقسیم بر 6 محدوده 0 - 5 را بیان میکند. #با افزودن 1، محدوده مورد نظر 1 - 6 تامین میگردد. #با تشکر از Paulo Marcel Coelho Aragao، برای سادهسازی. die1=0 die2=0 #بهتر نخواهد بود فقط SPOTS=7 تنظیم شود و 1 اضافه نشود؟ چرا یا چرا نه؟ # let "die1 = $RANDOM % $SPOTS +1" #انداختن تاس اول. let "die2 = $RANDOM % $SPOTS +1" #انداختن تاس دوم. # #+هستند، باقیمانده (%) یا افزایش (+) ؟ let "throw = $die1 + $die2" echo "Throw of the dice = $throw" echo exit 0
مثال 9-12. کشیدن یک کارت اتفاقی از یک دسته ورق
#!/bin/bash # # # Suites="Clubs Diamonds Hearts Spades" Denominations="2 3 4 5 6 7 8 9 10 Jack Queen King Ace" # suite=($Suites) # denomination=($Denominations) num_suites=${#suite[*]} # num_denominations=${#denomination[*]} echo -n "${denomination[$((RANDOM%num_denominations))]} of " echo ${suite[$((RANDOM%num_suites))]} # # #با تشکر از jipe برای نشان دادن این مورد استفاده از $RANDOM. exit 0
مثال 9-13. شبیهسازی حرکت Brownian
#!/bin/bash # #مولف: Mendel Cooper #تاریخ انتشار: 10/26/07 #مجوز: GPL3 # ---------------------------------------------------------------- #این اسکریپت حرکت Brownian را طرحریزی میکند: #+ #+ #+این اصطلاحاً «Drunkard's Walk»(حرکت مستانه) گفته میشود. # #+ #+ #+ #+ # .مترجم: در صورت تمایل، میتوانید یک شبیهسازی نمایشی زیبا از صفحه گالتون را در اینجامشاهده نمایید # # #+ #+ #به عنوان یک شبیهساز Galton Board، اسکریپت پارامترهایی از قبیل زاویه #+ #+ # # ---------------------------------------------------------------- PASSES=500 # ROWS=10 # RANGE=3 #محدوده 0 - 2 برای خروجی $RANDOM POS=0 # RANDOM=$$ # declare -a Slots # NUMSLOTS=21 # Initialize_Slots () { # for i in $( seq $NUMSLOTS ) do Slots[$i]=0 done echo # } Show_Slots () { echo; echo echo -n " " for i in $( seq $NUMSLOTS ) # do printf "%3d" ${Slots[$i]} # done echo # echo " |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|" echo " ||" echo # #+ #اجرا با تنها 500 گلوله، معمولاً مانع این امر میگردد. } Move () { # Move=$RANDOM #$RANDOM چطور تصادفی است؟ خوب، بیایید ببینیم let "Move %= RANGE" # case "$Move" in 0 ) ;; # 1 ) ((POS--));; # 2 ) ((POS++));; # * ) echo -n "Error ";; # esac } Play () { # i=0 while [ "$i" -lt "$ROWS" ] # do Move ((i++)); done SHIFT=11 #چرا 11 و نه 10؟ let "POS += $SHIFT" # (( Slots[$POS]++ )) #برای اشکالزدایی: echo $POS # } Run () { # p=0 while [ "$p" -lt "$PASSES" ] do Play (( p++ )) POS=0 # done } # -------------- # Initialize_Slots Run Show_Slots # -------------- exit $? # # #(1 نتایج را با نمودار میلهای عمودی، یا به صورت نمودار scattergram نمایش دهید. #(2 اسکریپت را برای استفاده از /dev/urandom به جای کاربرد $RANDOM اصلاح کنید. #آیا این کار، نتایج را بیشتر تصادفی خواهد نمود؟ #(3 نوعی «پویانمایی» یا خروجی گرافیکی برای حرکت هر گلوله ارایه کنید.
Jipe مجموعهای از تکنیکهای تولید اعداد تصادفی داخل یک محدوده را نشان میدهد.
#تولید عدد تصادفی بین 6 و 30. rnumber=$((RANDOM%25+6)) #تولید عدد تصادفی در همان محدوده 6 - 30 اما #+عدد باید بر 3 بخشپذیر باشد. rnumber=$(((RANDOM%30/3+1)*3)) # #اگر $RANDOM%30 صفر برگشت بدهد، ناموفق میشود. #Frank Wang جایگزین پایین را پیشنهاد میدهد: rnumber=$(( RANDOM%27/3*3+6 ))
Bill Gradwohl فرمول بهبودیافتهای که برای اعداد مثبت کار میکند، مطرح نموده است.
rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))
در اینجا Bill تابع فراگیرندهای را عرضه میکند که یک عدد تصادفی بین دو کمیت تعیین شده برگشت میدهد.
مثال 9-14. عدد تصادفی بین کمیتها
#!/bin/bash # # #اسکریپت توسط Bill Gradwohl، با اندک اصلاحاتی توسط نگارنده این سند. #اصلاحات در سطرهای 183 و 185 توسط Anthony Le Clezio است. # randomBetween() { #یک عدد تصادفی مثبت یا منفی بین $min و $max و قابل قسمت #+به $divisibleBy تولید میکند. # # # syntax() { # echo echo "Syntax: randomBetween [min] [max] [multiple]" echo echo -n "Expects up to 3 passed parameters, " echo "but all are completely optional." echo "min is the minimum value" echo "max is the maximum value" echo -n "multiple specifies that the answer must be " echo "a multiple of this value." echo " i.e. answer must be evenly divisible by this number." echo echo "If any value is missing, defaults area supplied as: 0 32767 1" echo -n "Successful completion returns 0, " echo "unsuccessful completion returns" echo "function syntax and 1." echo -n "The answer is returned in the global variable " echo "randomBetweenAnswer" echo -n "Negative values for any passed parameter are " echo "handled correctly." } local min=${1:-0} local max=${2:-32767} local divisibleBy=${3:-1} # local x local spread # [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy)) # if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o ${min} -eq ${max} ]; then syntax return 1 fi #بررسی اینکه min و max برعکس ارایه شدهاند if [ ${min} -gt ${max} ]; then # x=${min} min=${max} max=${x} fi #اگر خود min قابل قسمت به $divisibleBy نباشد، #+آنوقت اصلاح 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 #اگر خود max قابل قسمت به $divisibleBy نباشد، #+آنوقت اصلاح 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 # --------------------------------------------------------------------- # # #+ #+بین 0 و abs(max-min)+divisibleBy، نه abs(max-min)+1 باشد. # #تغییر دادن فرمول در جهت استفاده از abs(max-min)+1 بازهم جوابهای #+ #+تعداد دفعات برگشت داده شده برای نقاط انتهایی($min و $max) به طور #+ # --------------------------------------------------------------------- spread=$((max-min)) # #+ [ ${spread} -lt 0 ] && spread=$((0-spread)) let spread+=divisibleBy randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min)) return 0 #اما، Paulo Marcel Coelho Aragao اشاره میکند #+وقتی $max و $min قابل تقسیم به $divisibleBy #+ # # # } # min=-14 max=20 divisibleBy=3 # #+ 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 #اگر max خودش قابل قسمت به $divisibleBy نباشد، #+آنوقت اصلاح 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 # #+ disp=$((0-minimum)) for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do answer[i+disp]=0 done # loopIt=1000 # #+ for ((i=0; i<${loopIt}; ++i)); do #توجه نمایید که ما در اینجا min و max را به ترتیب برعکس #+ randomBetween ${max} ${min} ${divisibleBy} # [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] \ && echo MIN or MAX error - ${randomBetweenAnswer}! [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] \ && echo DIVISIBLE BY error - ${randomBetweenAnswer}! # answer[randomBetweenAnswer+disp]=$((answer[randomBetweenAnswer+disp]+1)) done # 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
$RANDOM چقدر تصادفی است؟ بهترین روش برای بررسی این مطلب نوشتن اسکریپتی است که توزیع اعداد «تصادفی» تولید شده توسط $RANDOM را پیگیری کند. بیایید چندین بار یک تاس $RANDOM را بریزیم . . .
مثال 9-15. غلتاندن یک تاس منفرد با RANDOM
#!/bin/bash # RANDOM=$$ #برپاسازی مجدد تولیدکننده عدد تصادفی با ID پردازش اسکریپت. PIPS=6 # MAXTHROWS=600 # throw=0 # ones=0 # twos=0 #+ 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++));; # 1) ((twos++));; # 2) ((threes++));; # 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 $? #با فرض اینکه RANDOM تصادفی است، امتیازهای توزیع شده باید نسبتاً مساوی باشند. #برای $MAXTHROWS=600 تمام گروهها باید حدود 100 بعلاوه یا منهای 20 باشند. # #به خاطر داشته باشید که RANDOM یک تولید کننده «شِبه تصادفی» #+ # # #+ # # --------------- # #طور چرخشی به هوا پرتاب کند. انتخابها "HEAD" و "TAIL" هستند.
همان طور که در مثال اخیر دیدهایم، بهترین کار، دوباره بنیاد کردن تولید کننده RANDOM در هر نوبت که فراخوانی میشود، است. به کار بردن همان بنیاد برای RANDOM، همان گروه اعداد را تکرار میکند. [2] ( این متغیر، رفتار تابع random() در C را بازتاب میدهد.)
مثال 9-16. دوباره بنیاد کردن RANDOM
#!/bin/bash # # MAXCOUNT=25 # 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 #تنظیم RANDOM تولید کننده عدد تصادفی را بنیاد میکند echo "Random seed = $SEED" random_numbers RANDOM=$SEED #همان بنیاد برای RANDOM . . . echo; echo "Again, with same random seed ..." echo "Random seed = $SEED" random_numbers #دقیقاً همان گروه اعداد را بازتولید میکند. # # echo; echo SEED=2 RANDOM=$SEED #تلاش دوباره، اما با یک بنیاد متفاوت . . . echo "Random seed = $SEED" random_numbers #. . . رشته اعداد متفاوتی ارایه میکند. echo; echo #RANDOM=$$ متغیر RANDOM را بواسطه شماره شناسایی پردازش اسکریپت بنیاد میکند. #همچنین، بنیاد کردن RANDOM بر اساس فرمانهای 'time' یا 'date' نیز مقدور است. #یک مورد تفننی... SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }'| sed s/^0*//) #خروجی شبه-تصادفی از /dev/urandom (فایل-دستگاه شبه-تصادفی سیستم) واکشی میشود #+سپس توسط "od" به سطر اعداد (اکتال) قابل چاپ تبدیل میشود آنوقت "awk" فقط یک #+عدد برای SEED بازیابی میکند، و سرانجام "sed" همه صفرهای مقدم را حذف میکند. RANDOM=$SEED echo "Random seed = $SEED" random_numbers echo; echo exit 0
فایل شبه-دستگاه /dev/urandom روشی برای تولید اعداد تصادفی ساختگی بسیار «تصادفی»تر از متغیر $RANDOM فراهم میکند. سطر فرمان dd if=/dev/urandom of=targetfile bs=1 count=XX یک فایلِ بخوبی پخش و پلا از اعداد تصادفی ساختگی تولید میکند. به هرحال، در یک اسکریپت تخصیص این اعداد به یک متغیر، به راهکاری موقتی از قبیل فیلتر کردن بواسطه od (همچون در مثال فوق، مثال 16-14، و مثال A-36)، یا حتی لولهکشی به md5sum (مثال 36-16 را ببینید) نیاز دارد.
روشهای دیگری نیز برای تولید اعداد تصادفی ساختگی در یک اسکریپت وجود دارد. Awk یک وسیله راحت برای انجام این کار فراهم میکند. مثال 9-17. اعداد تصادفی ساختگی، با استفاده از awk #!/bin/bash # فرمان date نیز برای تولید رشته های عدد صحیح شبه-تصادفی مناسب است. |
[1] | «تصادف» حقیقی در چنان درجهای اگر ابدا موجود باشد، تنها میتواند در پدیدههای طبیعی ناقص فهمیده شده از قبیل انهدام رادیواکتیو، یافت بشود. کامپیوترها فقط تصادف را شبیهسازی میکنند، و از این جهت رشته های اعداد «تصادفی» تولید کامپیوتر به عنوان شبه-تصادف منسوب میشوند. |
[2] |
بنیاد گروههای عددی شبه تصادفیِ تولیدِ کامپیوتر میتواند به عنوان یک برچسب هویت در نظر گرفته شود. به عنوان مثال، گروههای شبه-تصادفی با بنیاد 23 را به عنوان گروههای #23 تصور کنید. یک خصوصیت گروههای عدد شبه-تصادفی، طول چرخه قبل از شروع تکرار گروه است. یک تولید کننده شبه-تصادفی خوب، گروههایی با چرخههای بسیار طولانی ارایه میکند. |