Як генерувати випадкове число в Bash?


236

Як генерувати випадкове число в діапазоні в Bash?


21
Наскільки це потрібно випадково?
bdonlan

Відповіді:


283

Використовуйте $RANDOM. Це часто корисно в поєднанні з простою арифметикою оболонки. Наприклад, для генерації випадкового числа між 1 і 10 (включно):

$ echo $((1 + RANDOM % 10))
3

Фактичний генератор знаходиться в variables.c, функція brand(). Старіші версії були простим лінійним генератором. У версії 4.0 bashвикористовується генератор із цитатами на папір 1985 року, що, імовірно, означає, що це гідне джерело псевдовипадкових чисел. Я б не використовував це для моделювання (і, звичайно, не для криптовалюти), але це, ймовірно, достатньо для основних завдань сценарію.

Якщо ви робите щось, що вимагає серйозних випадкових чисел, ви можете використовувати /dev/randomабо /dev/urandomякщо вони доступні:

$ dd if=/dev/urandom count=4 bs=1 | od -t d

21
Будьте обережні тут. Незважаючи на те, що це вкрай дрібно, виконання арифметики на випадкових числах може різко вплинути на випадковість вашого результату. у випадку $RANDOM % 108 і 9 вимірно (хоча і незначно) менш вірогідні, ніж 0-7, навіть якщо $RANDOMє надійним джерелом випадкових даних.
dimo414

3
@ dimo414 Мені цікаво "незначно", чи є у вас джерело, де я можу дізнатися більше про це?
PascalVKooten

58
Модулюючи свій випадковий вхід, ви " голубіть " результати. Оскільки $RANDOMдіапазон «S є 0-32767число 0- 7карта для 3277різних можливих входів, але 8і 9можуть бути отримані тільки 3276різними способами (так як 32768і 32769неможливо). Це незначна проблема для швидких злому, але означає, що результат неоднаково випадковий. Випадкові бібліотеки, як і Java Random, пропонують функції правильного повернення уніфікованого випадкового числа в заданому діапазоні, а не просто модифікацію нероздільного числа.
dimo414

1
@JFSebastian дуже правда - проблема з модулем полягає в тому, що він може порушити рівномірність будь-яких RNG, не тільки поганих PRNG, але дякую, що виклик цього.
dimo414

14
Що стосується лише контексту, то для основного голуба для% 10 означає 8 та 9 приблизно на 0,03% менше, ніж 0–7. Якщо ваш скрипт оболонки вимагає більш точних рівномірних випадкових чисел, ніж це, то будь-якими способами використовуйте більш складний і правильний механізм.
Нельсон

70

Будь ласка, дивіться $RANDOM:

$RANDOM - це внутрішня функція Bash (не константа), яка повертає псевдовипадкове ціле число в діапазоні 0 - 32767. Її не слід використовувати для створення ключа шифрування.


1
Чи 32767має якесь особливе значення?
Джин Квон

14
@JinKwon 32767- 2^16 / 2 - 1це верхня межа для підписаного 16-бітного цілого числа.
Джефрі Мартінес

@JinKwon ви могли б пояснити, чому ви не скажете, що це так 2^15 - 1? Це рівнозначно, тому мені просто цікаво, чи є якийсь контекст, якого я пропускаю?
Бретт Холман

11
@BrettHolman Я думаю, що він намагався вказати на "підписану" частину підписаного 16-бітного цілого числа. 2 ^ 16 значень, розділених навпіл на позитивні та негативні вставки.
коді

Ця відповідь не дає відповіді на запитання.
Брат

48

Ви також можете використовувати shuf (доступний в coreutils).

shuf -i 1-100000 -n 1

Як ви проходите в vars як кінець діапазону? Я зрозумів це:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
Додайте $varзамість кінця діапазону, як це:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Я вважаю за краще цей варіант, оскільки легко генерувати N випадкових чисел за допомогою -n. Наприклад, генеруйте 5 випадкових чисел між 1 і 100 :shuf -i 1-100 -n 5
aerijman

Наскільки я розумію, цифри не випадкові. Якщо ви вкажете, shuf -i 1-10 -n 10ви отримаєте всі числа від 1 до 10 точних. Якщо ви вкажете, -n 15ви все одно отримаєте лише ті 10 номерів рівно один раз. Це насправді лише перетасовування, не генерування випадкових чисел.
радлан

Щоб отримати випадкові числа із заміною: -r
Джеффрі Андерсон

36

Спробуйте це з вашої оболонки:

$ od -A n -t d -N 1 /dev/urandom

Тут -t dзазначено, що вихідний формат повинен бути підписаний десятковою; -N 1каже прочитати один байт з /dev/urandom.


2
Питання задає числа в діапазоні.
JSycamore

9
ви можете видалити пробіли:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Роберт

23

ви також можете отримати випадкове число від awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 Ви знаєте, спочатку я хоч чому б вам коли-небудь хотів це робити так, але насправді мені це дуже подобається.
zelanix

1
Дякуємо за надання рішення, яке включає посів насіння. Я не міг його знайти ніде!
so.very.tired

2
+1 для висіву насіння. Можливо, варто згадати, що srand()насіння є поточним процесорним часом. Якщо вам потрібно вказати конкретне насіння, щоб RNG можна було дублювати, використовуйте, srand(x)де xє насіння. Крім того, цитується з посібника з числових функцій GNU awk, "різні реалізації awk використовують внутрішньо різні генератори випадкових чисел". Підсумок полягає в тому, що якщо ви зацікавлені в генеруванні статистичного розподілу, вам слід очікувати незначних змін, що переходять від однієї версії до другої на різних платформах (все це працює awkабо gawk).
Cbhihe


16

Мені подобається цей трюк:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...


7
$ RANDOM знаходиться в діапазоні від 0 до 32767. У вас з’явиться більше цифр, які починаються з 1, 2 або 3, ніж вам буде 4-9. Якщо ви добре з незбалансованим розподілом, це буде добре.
jbo5112

2
@ jbo5112 Ви абсолютно праві, як щодо відображення останньої цифри? echo $ {RANDOM: 0-1} для однієї цифри, $ {RANDOM: 0-2} для двозначної ...?
fraff

4
Якщо ви використовуєте останню цифру (цифри), вона буде досить хорошою, але вона буде включати 0 і 00. На одну цифру 0-7 траплятиметься на 0,03% частіше, ніж на 8-9. На двох цифрах 0-67 траплятиметься на 0,3% частіше, ніж 68-99. Якщо вам потрібна хороша розподіл випадкових чисел, сподіваємось, ви не використовуєте bash. Що стосується оригіналу: ${RANDOM:0:1}має 67,8% шансів дати вам 1 або 2, ${RANDOM:0:2}лише 0,03% шансу дати вам одноцифрове число (має бути 1%), і обидва мають шанс 0,003% дати вам 0 Ще є випадки використання, коли це нормально (наприклад, несумісне введення).
jbo5112

12

Випадкове число від 0 до 9 включно.

echo $((RANDOM%10))

2
Моє погано, я не прочитав належну сторінку. $RANDOMйде лише від 0 до 32767. Це повинно було сказати "Випадкове число здебільшого між 1 і 3, з кількома крилами";)
Девід Ньюкомб

1
Що? Це все ще буде від 0 до 9, хоча 8 і 9 матимуть трохи меншу ймовірність виникнення, ніж 0 до 7, як згадується в іншій відповіді.
кіні

6

Якщо ви використовуєте систему Linux, ви можете отримати випадкове число з / dev / random або / dev / urandom. Будьте обережні / dev / random заблокуються, якщо не буде достатньо випадкових чисел. Якщо вам потрібна швидкість над випадковістю, використовуйте / dev / urandom.

Ці "файли" будуть заповнені випадковими числами, згенерованими операційною системою. Це залежить від реалізації / dev / random у вашій системі, якщо ви отримаєте істинні чи псевдо випадкові числа. Справжні випадкові цифри генеруються за допомогою форми шуму, зібраного від драйверів пристроїв, таких як миша, жорсткий диск, мережа.

Ви можете отримати випадкові числа з файлу за допомогою dd


5

Я взяв кілька таких ідей і зробив функцію, яка повинна виконуватись швидко, якщо потрібно багато випадкових чисел.

дзвінок odдорогий, якщо вам потрібно багато випадкових номерів. Натомість я називаю це один раз і зберігаю 1024 випадкових числа з / dev / urandom. Коли randвикликається, останнє випадкове число повертається та масштабується. Потім він видаляється з кешу. Коли кеш порожній, зчитується ще 1024 випадкових числа.

Приклад:

rand 10; echo $RET

Повертає випадкове число в RET від 0 до 9 включно.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

ОНОВЛЕННЯ: Це працює не так добре для всіх N. Він також витрачає випадкові біти, якщо використовується з малим N. Зауваживши, що (у цьому випадку) 32-бітове випадкове число має достатню ентропію для 9 випадкових чисел між 0 і 9 (10 * 9 = 1 000 000 000 <= 2 * 32), ми можемо отримати кілька випадкових чисел з кожного 32 випадкового значення джерела.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done

Я спробував це - це зайняло 10 секунд 100% процесора, а потім надрукувало 10 чисел, які не виглядали випадковими ВСІМ.
Карло Вуд

Я зараз пам’ятаю. Цей код генерує 100 000 випадкових чисел. Він ставить кожного в «відро», щоб подивитися, наскільки він випадковий. Є 10 бункерів. Ці числа повинні бути подібними, якщо кожне випадкове число між 0 і 9 однаково вірогідне. Якщо ви хочете надрукувати кожен номер, відлучіть $ RET після randBart between 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomбуде генерувати 10 випадкових непідписаних 32-бітових цілих чисел, розділених пробілом. ви можете зберігати його в масиві та використовувати його згодом. ваш код здається надмірним.
Алі

@Ali, OP не вказала, що вони хочуть 32-бітове або будь-яке інше розмірне випадкове число. Я та деякі інші тлумачили це питання як надання випадкового числа в межах діапазону. Моя функція rand досягає цієї мети, а також зменшує втрату ентропії, яка при вичерпанні спричиняє блокування програм. od on / dev / urandom повертає лише 2 ^ N довільних випадкових чисел, і тоді OP потрібно буде зберігати декілька значень у масив, послідовно вилучаючи їх з цього масиву та поповнюючи цей масив. Можливо, ви можете кодувати це як відповідь і обробляти інші діапазони випадкових чисел?
philcolbourn

@philcolbourn, ви вірно відноситесь до ОП, не вказуючи, який саме випадковий номер він хоче, і це мені не вистачило уваги. Але він тільки запитав: «Як створити в випадкове число в Баш?». Моя думка, що він запитував лише одне випадкове число. Хоча цей критик стосується і мого попереднього коментаря (генеруючи 10 випадкових чисел).
Алі

5

Читання спеціальних файлів символів / dev / random або / dev / urandom - це шлях.

Ці пристрої повертають справді випадкові номери під час читання, і вони розроблені, щоб допомогти програмному забезпеченню вибрати безпечні ключі для шифрування. Такі випадкові числа витягуються з пулу ентропії, який сприяють різні випадкові події. {LDD3, Джонатан Корбет, Алессандро Рубіні та Грег Кроа-Хартман]

Ці два файли, зокрема, є інтерфейсом до рандомізації ядра

void get_random_bytes_arch(void* buf, int nbytes)

який малює справді випадкові байти з апаратних засобів, якщо така функція виконується апаратним шляхом (як правило, є), або вона черпає з пулу ентропії (що складається з таймінгів між подіями, такими як перерви миші та клавіатури та інші переривання, зареєстровані SA_SAMPLE_RANDOM).

dd if=/dev/urandom count=4 bs=1 | od -t d

Це працює, але записує непотрібний вихід з ddв stdout. Команда нижче дає лише ціле число, яке мені потрібно. Я навіть можу отримати вказану кількість випадкових бітів, як мені потрібно, коригуючи бітову маску, задану для арифметичного розширення:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))

3

А як на рахунок:

perl -e 'print int rand 10, "\n"; '

1
Для криптографічно захищеного випадкового числа вам потрібно читати з / dev / urandom або використовувати бібліотеки Crypt :: Random.
кк

3

Можливо, я трохи пізно, але що робити з використанням jotгенерувати випадкове число в межах діапазону в Bash?

jot -r -p 3 1 0 1

Це генерує випадкове ( -r) число з точністю до 3 знаків після коми ( -p). У цьому конкретному випадку ви отримаєте одне число від 0 до 1 ( 1 0 1). Ви також можете друкувати послідовні дані. Джерелом випадкового числа, згідно з посібником, є:

Випадкові числа отримують через arc4random (3), коли не вказано насіння, і через випадкове (3), коли дається насіння.


1
Потрібно встановити: sudo apt install athena-jot
xerostomus

3

На основі чудових відповідей @Nelson, @Barun та @Robert, ось сценарій Bash, який генерує випадкові числа.

  • Можна створити кількість цифр, які ви хочете.
  • кожна цифра генерується окремо, /dev/urandomщо набагато краще, ніж вбудована Баша$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num

Саме те, що я був після.
Прометей

2

Створити випадкове число в діапазоні від 0 до n (підписане 16-бітове ціле число). Результат встановлений у змінній $ RAND. Наприклад:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done

1

Випадкове розгалуження програми або так / ні; 1/0; істинний / хибний вихід:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

якщо вам лінь згадати 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.