П О Р Т А Л                            
С Е Т Е В Ы Х                          
П Р О Е К Т О В                        
  
Поиск по сайту:
                                                 
Главная

О проекте

Web-мастеру
     HTML & JavaScript
     SSI
     Perl
     PHP
     XML & XSLT
     Unix Shell

MySQL

Безопасность

Хостинг

Другое








Самое читаемое:

Учебник PHP - "Для Чайника".
Просмотров 3513 раз(а).

Иллюстрированный самоучитель по созданию сайтов.
Просмотров 6118 раз(а).

Учебник HTML.
Просмотров 3272 раз(а).

Руководство по PHP5.
Просмотров 5492 раз(а).

Хостинг через призму DNS.
Просмотров 4134 раз(а).

Подборка текстов стандартных документов.
Просмотров 55769 раз(а).

Учебник PHP - Самоучитель
Просмотров 3085 раз(а).

Документация на MySQL (учебник & справочное руководство)
Просмотров 5655 раз(а).

Внешние атаки...
Просмотров 3836 раз(а).

Учебник PHP.
Просмотров 2822 раз(а).

SSI в примерах.
Просмотров 37459 раз(а).



 
 
| Добавить в избранное | Сделать стартовой | Помощь





9.6. $RANDOM: генерация псевдослучайных целых чисел

$RANDOM -- внутренняя функция Bash (не константа), которая возвращает псевдослучайные целые числа в диапазоне 0 - 32767. Функция $RANDOM не должна использоваться для генераци ключей шифрования.

Пример 9-23. Генерация случайных чисел

#!/bin/bash

# $RANDOM возвращает различные случайные числа при каждом обращении к ней.
# Диапазон изменения: 0 - 32767 (16-битовое целое со знаком).

MAXCOUNT=10
count=1

echo
echo "$MAXCOUNT случайных чисел:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ]      # Генерация 10 ($MAXCOUNT) случайных чисел.
do
  number=$RANDOM
  echo $number
  let "count += 1"  # Нарастить счетчик.
done
echo "-----------------"

# Если вам нужны случайные числа не превышающие определенного числа,
# воспользуйтесь оператором деления по модулю (остаток от деления).

RANGE=500

echo

number=$RANDOM
let "number %= $RANGE"
echo "Случайное число меньше $RANGE  ---  $number"

echo

# Если вы желаете ограничить диапазон "снизу",
# то просто производите генерацию псевдослучайных чисел в цикле до тех пор,
# пока не получите число большее нижней границы.

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
Note

Jipe подсказал еще один способ генерации случайных чисел из заданного диапазона.

#  Генерация случайных чисел в диапазоне 6 - 30.
rnumber=$((RANDOM%25+6))

#  Генерируется случайное число из диапазона 6 - 30,
#+ но при этом число должно делиться на 3 без остатка.
rnumber=$(((RANDOM%30/3+1)*3))

# Примечательно, если $RANDOM возвращает 0
# то это приводит к возникновению ошибки.

#  Упражнение: Попробуйте разобраться с выражением самостоятельно.


Bill Gradwohl предложил усовершенствованную формулу генерации положительных псевдослучайных чисел в заданном диапазоне.

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


В сценарии ниже, Bill представил универсальную функцию, которая возвращает псевдослучайное число из заданного диапазона.

Пример 9-25. Псевдослучайное число из заданного диапазона

#!/bin/bash
# random-between.sh
# Псевдослучайное число из заданного диапазона.
# Автор: Bill Gradwohl,
# незначительные изменения внесены автором документа.
# Используется с разрешения автора сценария.


randomBetween() {
   #  Генерация положительных и отрицательных псевдослучайных чисел,
   #+ в диапазоне от $min до $max,
   #+ которые кратны числу $divisibleBy.
   #
   #  Bill Gradwohl - Oct 1, 2003

   syntax() {
   # Вложенная функция.
      echo
      echo    "Порядок вызова: randomBetween [min] [max] [multiple]"
      echo
      echo    "Функция ожидает до 3-х входных аргументов, но они не являются обязательными."
      echo    "min -- нижняя граница диапазона"
      echo    "max -- верхняя граница диапазона"
      echo    "multiple -- делитель, на который должен делиться результат без остатка."
      echo
      echo    "Если какой либо из параметров отсутствует, по-умолчанию принимаются значения: 0 32767 1"
      echo    "В случае успеха функция возвращает 0, иначе -- 1"
      echo    "и это сообщение о порядке вызова."
      echo    "Результат возвращается в глобальной переменной randomBetweenAnswer"
      echo    "Входные параметры, имеющие отрицательные значения, обрабатываются корректно."
   }

   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,
   #+ то привести его к ближайшему подходящему числу из заданного диапазона.
   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,
   #+ то привести его к ближайшему подходящему числу из заданного диапазона.
   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,
   #+ то будут получаться вполне корректные значения, но при этом,
   #+ возвращаемые значения будут значительно ниже
   #+ верхней границы диапазона.
   #  ---------------------------------------------------------------------

   spread=$((max-min))
   [ ${spread} -lt 0 ] && spread=$((0-spread))
   let spread+=divisibleBy
   randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min))

   return 0
}

# Проверка функции.
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,
   #+ то привести его к ближайшему подходящему числу из заданного диапазона.

   if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then
      if [ ${maximum} -lt 0 ]; then
         maximum=$((((maximum/divisibleBy)-1)*divisibleBy))
      else
         maximum=$((maximum/divisibleBy*divisibleBy))
      fi
   fi


#  Необходимое условие при работе с массивами --
#+ индекс массива должен быть положительным числом,
#+ поэтому введена дополнительная переменная displacement, которая
#+ гарантирует положительность индексов.

displacement=$((0-minimum))
for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
   answer[i+displacement]=0
done


# Цикл с большим количеством итераций, чтобы посмотреть -- что мы получаем.
loopIt=1000   #  Автор сценария предложил 100000 итераций,
              #+ но в этом случае цикл работает чересчур долго.

for ((i=0; i<${loopIt}; ++i)); do

   #  Обратите внимание: числа min и max передаются функции в обратном порядке,
   #+ чтобы продемонстрировать, что функция обрабатывает их корректно.

   randomBetween ${max} ${min} ${divisibleBy}

   # Вывод сообщения об ошибке, если функция вернула некорректное значение.
   [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] && echo Выход за границы диапазона MIN .. MAX  - ${randomBetweenAnswer}!
   [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] && echo Число не делится на заданный делитель без остатка - ${randomBetweenAnswer}!

   # Записать полученное число в массив.
   answer[randomBetweenAnswer+displacement]=$((answer[randomBetweenAnswer+displacement]+1))
done



# Проверим полученные результаты

for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
   [ ${answer[i+displacement]} -eq 0 ] && echo "Число $i не было получено ни разу." || echo "Число ${i} встречено ${answer[i+displacement]} раз."
done


exit 0

Насколько случайны числа, возвращаемые функцией $RANDOM? Лучший способ оценить "случайность" генерируемых чисел -- это написать сценарий, который будет имитировать бросание игрального кубика достаточно большое число раз, а затем выведет количество выпадений каждой из граней...

Пример 9-26. Имитация бросания кубика с помощью 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-27. Переустановка 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

Note

Системный генератор /dev/urandom дает последовательность псевдослучайных чисел с более равномерным распределением, чем $RANDOM. Команда dd if=/dev/urandom of=targetfile bs=1 count=XX создает файл, содержащий последовательность псевдослучайных чисел. Однако, эти числа требуют дополнительной обработки, например с помощью команды od (этот прием используется в примере выше) или dd (см. Пример 12-45).

Есть и другие способы генерации псевдослучайных последовательностей в сценариях. Awk имеет для этого достаточно удобные средства.

Пример 9-28. Получение псевдослучайных чисел с помощью awk

#!/bin/bash
# random2.sh: Генерация псевдослучайных чисел в диапазоне 0 - 1.
# Используется функция rand() из awk.

AWKSCRIPT=' { srand(); print rand() } '
# Команды/параметры, передаваемые awk
# Обратите внимание, функция srand() переустанавливает начальное число генератора случайных чисел.

echo -n "Случайное число в диапазоне от 0 до 1 = "
echo | awk "$AWKSCRIPT"

exit 0


# Упражнения:
# ---------

# 1) С помощью оператора цикла выведите 10 различных случайных чисел.
#      (Подсказка: вам потребуется вызвать функцию "srand()"
#      в каждом цикле с разными начальными числами.
#      Что произойдет, если этого не сделать?)

# 2) Заставьте сценарий генерировать случайные числа в диапазоне 10 - 100
#      используя целочисленный множитель, как коэффициент масштабирования

# 3) То же самое, что и во втором упражнении,
#      но на этот раз случайные числа должны быть целыми.

Кроме того, команда date так же может использоваться для генерации псевдослучайных целых чисел.

Назад | Вперед
Содержание (общее) | Содержание раздела | Содержание подраздела



Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту: