Генерация случайных чисел в определенном диапазоне

84

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

Я читал о /dev/random, /dev/urandomи $RANDOM, но ни один из них не может сделать то, что мне нужно.

Есть ли другая полезная команда или способ использовать предыдущие данные?

BowPark
источник
2
См. Этот раздел вопросов и ответов: stackoverflow.com/questions/8988824/…, а также этот stackoverflow.com/questions/2556190/… .
SLM
Возможно , связанные с : unix.stackexchange.com/questions/42045/random-number-needed/...
ОДС
Связанный: unix.stackexchange.com/questions/124478/...
ОДС

Ответы:

46

В инструментарии POSIX вы можете использовать awk:

awk -v min=5 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

Как не использовать его в качестве источника для генерации паролей или секретные данные , например, как и в большинстве awkреализаций, число легко можно догадаться на основе времени эта команда была запущена.

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

Стефан Шазелас
источник
1
+1 .. Как используется в сценарии для назначения вывода переменной (без введения символа новой строки и использования нулевого заполнения для двух мест):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Кристофер
Это основано на времени? так как я получил то же значение в 2 последовательных прогонах ...
Шай Алон
@ShaiAlon текущее время часто используется как начальное значение по умолчанию для srand (), поэтому обычно да, оно основано на времени, см. Предложение Стефана об этом в его ответе. Вы можете обойти это, изменив начальное начальное число на случайное число с помощью awk -v min=5 -v max=10 -v seed="$(od -An -N4 -tu4 /dev/urandom)" 'BEGIN{srand(seed+0); print int(min+rand()*(max-min+1))}'. Вы можете использовать $RANDOMвместо, od ... /dev/randomно тогда ваше начальное значение находится в относительно небольшом диапазоне от 0 до 32767, и, таким образом, вы, вероятно, получите заметные повторения в выходных данных со временем.
Эд Мортон
142

Вы можете попробовать shufиз GNU coreutils:

shuf -i 1-100 -n 1
cuonglm
источник
Это также работает с Busybox shuf.
СОУ
Работает также в raspbian и GitBash.
nPcomp
30

йота

В BSD и OSX вы можете использовать jot для возврата единственного числа random ( -r) из интервала minв maxвключительно.

$ min=5
$ max=10
$ jot -r 1 $min $max

Проблема распределения

К сожалению, на диапазон и распределение случайно сгенерированных чисел влияет тот факт, что jot внутренне использует арифметику с плавающей запятой двойной точности и printf (3) для формата вывода, что вызывает проблемы с округлением и усечением. Поэтому интервалы minи maxгенерируются реже, как показано:

$ jot -r 100000 5 10 | sort -n | uniq -c
9918  5
20176 6
20006 7
20083 8
19879 9
9938  10

На OS X 10.11 (El Capitan) это, кажется, было исправлено:

$ jot -r 100000 5 10 | sort -n | uniq -c
16692 5
16550 6
16856 7
16579 8
16714 9
16609 10  

а также...

$ jot -r 1000000 1 10 | sort -n | uniq -c
100430 1
99965 2
99982 3
99796 4
100444 5
99853 6
99835 7
100397 8
99588 9
99710 10

Решение проблемы распределения

Для более старых версий OS X, к счастью, есть несколько обходных путей. Одним из них является использование printf (3) целочисленного преобразования. Единственное предостережение в том, что максимальный интервал теперь становится max+1. Используя целочисленное форматирование, мы получаем справедливое распределение по всему интервалу:

$ jot -w %i -r 100000 5 11 | sort -n | uniq -c
16756 5
16571 6
16744 7
16605 8
16683 9
16641 10

Идеальное решение

И, наконец, чтобы получить правильное бросок костей с помощью обходного пути, мы имеем:

$ min=5
$ max_plus1=11  # 10 + 1
$ jot -w %i -r 1 $min $max_plus1

Дополнительная домашняя работа

См. Jot (1) для подробной информации о математике и форматировании, а также многих других примеров.

Клинт Пахл
источник
15

$RANDOMПеременный обычно не является хорошим способ генерируемых хороших случайных значений. Выходные данные /dev/[u]randomнеобходимо также преобразовать в первую очередь.

Проще всего использовать языки более высокого уровня, например, python:

Чтобы сгенерировать случайную целочисленную переменную от 5 до 10 (5 <= N <= 10), используйте

python -c "import random; print random.randint(5,10)"

Не используйте это для криптографических приложений.

jofel
источник
Это было полезно. Большое спасибо :) Поскольку подпись часто работала с Unix-> Solaris 10, утилиты GNU по умолчанию не интегрированы. Это сработало однако.
терпение
11

Вы можете получить случайное число через urandom

head -200 /dev/urandom | cksum

Выход:

3310670062 52870

Чтобы получить одну часть вышеуказанного номера.

head -200 /dev/urandom | cksum | cut -f1 -d " "

Тогда вывод

3310670062

zangw
источник
head -200(или его эквивалент POSIX head -n 100) возвращает первые 200 строк из /dev/urandom. /dev/urandomэто не текстовый файл, эта команда может возвращать от 200 байтов (все 0x0a, LF в ASCII) до бесконечности (если байт 0xa никогда не возвращается), но значения между 45k и 55k являются наиболее вероятными. cksum возвращает 32-битное число, поэтому бессмысленно получать более 4 байтов /dev/urandom.
Стефан Шазелас
7

Чтобы сгенерировать случайную целочисленную переменную от 5 до 10 (включая оба), используйте

echo $(( RANDOM % (10 - 5 + 1 ) + 5 ))

% работает как оператор по модулю.

Вероятно, есть более эффективные способы преобразования случайной величины $RANDOMв определенный диапазон. Не используйте это для криптографических приложений или в случаях, когда вам нужны реальные, равномерно распределенные случайные величины (например, для моделирования).

jofel
источник
3
Во многих реализациях оболочки, которые имеют $ RANDOM (особенно старые, особенно bash), это не очень случайно. В большинстве оболочек это число находится в диапазоне от 0 до 65535, поэтому любой диапазон, ширина которого не является степенью двойки, будет иметь дискретность распределения вероятностей. (в этом случае числа с 5 по 8 будут иметь вероятность 10923/65536, а у 9 и 10 будет вероятность 10922/65536). Чем шире диапазон, тем больше дискретность.
Стефан Шазелас
Извините, это 32767, а не 65535, поэтому приведенный выше расчет неверен (и на самом деле он хуже).
Стефан Шазелас
@ StéphaneChazelas Я имел в виду это, когда писал, что есть лучшие способы ...
jofel
5

# echo $(( $RANDOM % 256 )) выдаст «случайное» число от 0 до 255 на современных диалектах.

qrkourier
источник
Похож на этот другой ответ от 2 лет назад.
Джефф Шаллер
1
Это зависит от проблемы с «голубым отверстием» (TL; DR: некоторые результаты более вероятны, чем другие), если вместо 256 вы используете значение, которое не делит 32768 поровну.
toon81
4

Может быть UUID(в Linux) можно использовать для получения случайного числа

$ cat /proc/sys/kernel/random/uuid
cdd52826-327d-4355-9737-895f58ad11b4

Чтобы получить случайное число между 70и100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid
zangw
источник
3
cat /dev/urandom | tr -dc 'a-fA-F0-9' | fold -w 8 | head -n 1

Это сгенерирует шестнадцатеричное число длиной 8 цифр.

Том Купер
источник
1

Чтобы полностью остаться в bash и использовать переменную $ RANDOM, но избегайте неравномерного распределения:

#!/bin/bash
range=10 
floor=20

if [ $range -gt 32768 ]; then
echo 'range outside of what $RANDOM can provide.' >&2
exit 1 # exit before entering infinite loop
fi 

max_RANDOM=$(( 2**15/$range*$range ))
r=$RANDOM
until [ $r -lt $max_RANDOM ]; do
r=$RANDOM
done
echo $(( r % $range + $floor ))

Этот пример предоставит случайные числа от 20 до 29.

Чем ангел
источник
$rследует инициализировать до 65535 или другого высокого значения, чтобы избежать [: -lt: unary operator expectedошибки.
LawrenceC
Исправлена ​​инициализация $ r перед проверкой цикла. Спасибо @LawrenceC!
Чем Энджелл
0

Распределение это хорошо:

for ((i = 1; i <= 100000; i ++)) do echo $ ((RANDOM% (20 - 10 + 1) + 10)); сделано | сортировать -n | uniq -c

значение счета

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

SteveK
источник