Искусство программирования на языке сценариев командной оболочки - Мендель Купер
Шрифт:
Интервал:
Закладка:
# Если вы желаете ограничить диапазон "снизу",
# то просто производите генерацию псевдослучайных чисел в цикле до тех пор,
# пока не получите число большее нижней границы.
FLOOR=200
number=0 # инициализация
while [ "$number" -le $FLOOR ]
do
number=$RANDOM
done
echo "Случайное число, большее $FLOOR --- $number"
echo
# Эти два способа могут быть скомбинированы.
number=0 #initialize
while [ "$number" -le $FLOOR ]
do
number=$RANDOM
let "number %= $RANGE" # Ограничение "сверху" числом $RANGE.
done
echo "Случайное число в диапазоне от $FLOOR до $RANGE --- $number"
echo
# Генерация случайных "true" и "false" значений.
BINARY=2
number=$RANDOM
T=1
let "number %= $BINARY"
# let "number >>= 14" дает более равномерное распределение
# (сдвиг вправо смещает старший бит на нулевую позицию, остальные биты обнуляются).
if [ "$number" -eq $T ]
then
echo "TRUE"
else
echo "FALSE"
fi
echo
# Можно имитировать бросание 2-х игровых кубиков.
SPOTS=7 # остаток от деления на 7 дает диапазон 0 - 6.
ZERO=0
die1=0
die2=0
# Кубики "выбрасываются" раздельно.
while [ "$die1" -eq $ZERO ] # Пока на "кубике" ноль.
do
let "die1 = $RANDOM % $SPOTS" # Имитировать бросок первого кубика.
done
while [ "$die2" -eq $ZERO ]
do
let "die2 = $RANDOM % $SPOTS" # Имитировать бросок второго кубика.
done
let "throw = $die1 + $die2"
echo "Результат броска кубиков = $throw"
echo
exit 0
Пример 9-24. Выбор случайной карты из колоды
#!/bin/bash
# pick-card.sh
# Пример выбора случайного элемента массива.
# Выбор случайной карты из колоды.
Suites="Треф
Бубей
Червей
Пик"
Denominations="2
3
4
5
6
7
8
9
10
Валет
Дама
Король
Туз"
suite=($Suites) # Инициализация массивов.
denomination=($Denominations)
num_suites=${#suite[*]} # Количество элементов массивов.
num_denominations=${#denomination[*]}
echo -n "${denomination[$((RANDOM%num_denominations))]} "
echo ${suite[$((RANDOM%num_suites))]}
# $bozo sh pick-cards.sh
# Валет Треф
# Спасибо "jipe," за пояснения по работе с $RANDOM.
exit 0
Jipe подсказал еще один способ генерации случайных чисел из заданного диапазона.
# Генерация случайных чисел в диапазоне 6 - 30.
rnumber=$((RANDOM%25+6))
# Генерируется случайное число из диапазона 6 - 30,
#+ но при этом число должно делиться на 3 без остатка.
rnumber=$(((RANDOM%30/3+1)*3))
# Упражнение: Попробуйте разобраться с выражением самостоятельно.
Насколько случайны числа, возвращаемые функцией $RANDOM? Лучший способ оценить "случайность" генерируемых чисел -- это написать сценарий, который будет имитировать бросание игрального кубика достаточно большое число раз, а затем выведет количество выпадений каждой из граней...
Пример 9-25. Имитация бросания кубика с помощью RANDOM
#!/bin/bash
# Случайные ли числа возвращает RANDOM?
RANDOM=$$ # Инициализация генератора случайных чисел числом PID процесса-сценария.
PIPS=6 # Кубик имеет 6 граней.
MAXTHROWS=600 # Можете увеличить, если не знаете куда девать свое время.
throw=0 # Счетчик бросков.
zeroes=0 # Обнулить счетчики выпадения отдельных граней.
ones=0 # т.к. неинициализированные переменные - "пустые", и не равны нулю!.
twos=0
threes=0
fours=0
fives=0
sixes=0
print_result ()
{
echo
echo "единиц = $ones"
echo "двоек = $twos"
echo "троек = $threes"
echo "четверок = $fours"
echo "пятерок = $fives"
echo "шестерок = $sixes"
echo
}
update_count()
{
case "$1" in
0) let "ones += 1";; # 0 соответствует грани "1".
1) let "twos += 1";; # 1 соответствует грани "2", и так далее
2) let "threes += 1";;
3) let "fours += 1";;
4) let "fives += 1";;
5) let "sixes += 1";;
esac
}
echo
while [ "$throw" -lt "$MAXTHROWS" ]
do
let "die1 = RANDOM % $PIPS"
update_count $die1
let "throw += 1"
done
print_result
# Количество выпадений каждой из граней должно быть примерно одинаковым, если считать RANDOM достаточно случайным.
# Для $MAXTHROWS = 600, каждая грань должна выпасть примерно 100 раз (плюс-минус 20).
#
# Имейте ввиду, что RANDOM - это генератор ПСЕВДОСЛУЧАЙНЫХ чисел,
# Упражнение:
# ---------------
# Перепишите этот сценарий так, чтобы он имитировал 1000 бросков монеты.
# На каждом броске возможен один из двух вариантов выпадения - "ОРЕЛ" или "РЕШКА".
exit 0
Как видно из последнего примера, неплохо было бы производить переустановку начального числа генератора случайных чисел RANDOM перед тем, как начать работу с ним. Если используется одно и то же начальное число, то генератор RANDOM будет выдавать одну и ту же последовательность чисел. (Это совпадает с поведением функции random() в языке C.)
Пример 9-26. Переустановка RANDOM
#!/bin/bash
# seeding-random.sh: Переустановка переменной RANDOM.
MAXCOUNT=25 # Длина генерируемой последовательности чисел.
random_numbers ()
{
count=0
while [ "$count" -lt "$MAXCOUNT" ]
do
number=$RANDOM
echo -n "$number "
let "count += 1"
done
}
echo; echo
RANDOM=1 # Переустановка начального числа генератора случайных чисел RANDOM.
random_numbers
echo; echo
RANDOM=1 # То же самое начальное число...
random_numbers # ...в результате получается та же последовательность чисел.
#
# В каких случаях может оказаться полезной генерация совпадающих серий?
echo; echo
RANDOM=2 # Еще одна попытка, но с другим начальным числом...
random_numbers # получим другую последовательность.
echo; echo
# RANDOM=$$ в качестве начального числа выбирается PID процесса-сценария.
# Вполне допустимо взять в качестве начального числа результат работы команд 'time' или 'date'.
# Немного воображения...
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
# Псевдослучайное число забирается
#+ из системного генератора псевдослучайных чисел /dev/urandom ,
#+ затем конвертируется в восьмеричное число командой "od",
#+ и наконец "awk" возвращает единственное число для переменной SEED.
RANDOM=$SEED
random_numbers
echo; echo
exit 0
Системный генератор /dev/urandom дает последовательность псевдослучайных чисел с более равномерным распределением, чем $RANDOM. Команда dd if=/dev/urandom of=targetfile bs=1 count=XX создает файл, содержащий последовательность псевдослучайных чисел. Однако, эти числа требуют дополнительной обработки, например с помощью команды od (этот прием используется в примере выше) или dd (см. Пример 12-42).
Есть и другие способы генерации псевдослучайных последовательностей в сценариях. Awk имеет для этого достаточно удобные средства.
Пример 9-27. Получение псевдослучайных чисел с помощью awk
#!/bin/bash