Створення випадкових чисел у визначеному діапазоні


84

Після трохи поглинання я не зміг знайти простий спосіб використовувати команду shell для генерації випадкового цілого числа, що входить до певного діапазону, тобто між мінімумом і максимумом.

Я читав про /dev/random, /dev/urandomі $RANDOM, але жодне з них не може робити те, що мені потрібно.

Чи є інша корисна команда чи спосіб використовувати попередні дані?


2
Ознайомтеся з цим питанням і відповідей: stackoverflow.com/questions/8988824/… , а також цей stackoverflow.com/questions/2556190/… .
slm


Відповіді:


45

У інструментальному інструменті 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 (), тому зазвичай, так, це базується на часі, див. Пропозицію Stéphane про це у своїй відповіді. Ви можете обійти це, змінивши початкове насіння на випадкове число на 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, тому ви, ймовірно, отримаєте помітні повтори у своєму результаті з часом.
Ед Мортон

141

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

shuf -i 1-100 -n 1

Це також працює з Busybox shuf.
ков

Працює і в raspbian і GitBash.
nPcomp

30

джот

На BSD та OSX ви можете використовувати jot, щоб повернути одне випадкове ( -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

У ОС 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)"

Не використовуйте це для криптографічних програм.


Це було корисно. Дякую дуже :) Як підписаний часто працював з Unix-> Solaris 10, утиліти GNU не інтегровані за замовчуванням. Однак це спрацювало.
пропатія

11

Ви можете отримати випадкове число urandom

head -200 /dev/urandom | cksum

Вихід:

3310670062 52870

Щоб отримати одну частину вищевказаного числа.

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

Тоді вихід є

3310670062


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 у певний діапазон. Не використовуйте це для криптографічних програм або у випадках, коли вам потрібні реальні, однаково розподілені випадкові змінні (наприклад, для моделювання).


3
У багатьох реалізаціях оболонок, у яких є $ RANDOM (особливо старіші, особливо bash), це не дуже випадково. У більшості оболонок число становить від 0 до 65535, тому будь-який діапазон, ширина якого не має сили двох, матиме невідповідність розподілу ймовірностей. (у цьому випадку цифри від 5 до 8 матимуть ймовірність 10923/65536, тоді як 9 і 10 матимуть ймовірність 10922/65536). Чим ширший асортимент, тим більша розбіжність.
Stéphane Chazelas

Вибачте, це 32767, а не 65535, тому розрахунок вище неправильний (і насправді гірший).
Стефан Шазелас

@ StéphaneChazelas Я мав це на увазі, коли писав, що є кращі способи ...
jofel

5

# echo $(( $RANDOM % 256 )) створить "випадкове" число між 0-255 у сучасних * sh діалектах.


Схоже на цю іншу відповідь ще 2 роки тому.
Jeff Schaller

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

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

Розподіл хороший:

для ((i = 1; i <= 100000; i ++)) виконувати ехо $ ((РІДНИЙ% (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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.