Як ефективно генерувати великі, рівномірно розподілені випадкові цілі числа в баші?


30

Мені було цікаво, що було б найкращим способом отримати добру випадковість у баші, тобто якою буде процедура отримання випадкового додатного цілого числа між MINта MAXтаким, що

  1. Діапазон може бути довільно великим (або принаймні, скажімо, до 2 32 -1);
  2. Значення рівномірно розподілені (тобто відсутність упередженості);
  3. Це ефективно.

Ефективний спосіб отримати випадковість в баші - це використовувати $RANDOMзмінну. Однак це лише вибірки значення між 0 і 2 15 -1, яке може бути недостатньо великим для всіх цілей. Люди, як правило, використовують модуль, щоб отримати його в потрібному діапазоні, наприклад,

MIN=0
MAX=12345
rnd=$(( $RANDOM % ($MAX + 1 - $MIN) + $MIN ))

Це, крім того, створює упередження, якщо не $MAXтрапиться поділ 2 15 -1 = 32767. Наприклад, якщо $MINце 0 і $MAXдорівнює 9, то значення 0 до 7 є трохи більш імовірними, ніж значення 8 і 9, оскільки $RANDOMніколи не буде 32768 або 32769. Це зміщення погіршується в міру збільшення діапазону, наприклад, якщо $MIN0 і $MAXє 9999, а потім цифри від 0 до 2767 мають ймовірність 4 / 32767 , в той час як число 2768 до 9999 є тільки ймовірність 3 / 32767 .

Отже, хоча зазначений вище метод відповідає умові 3, він не відповідає умовам 1 і 2.

Найкращим методом, який я придумав поки що, намагаючись задовольнити умови 1 і 2, було використовувати /dev/urandomнаступний:

MIN=0
MAX=1234567890
while
  rnd=$(cat /dev/urandom | tr -dc 0-9 | fold -w${#MAX} | head -1 | sed 's/^0*//;')
  [ -z $rnd ] && rnd=0
  (( $rnd < $MIN || $rnd > $MAX ))
do :
done

В основному, просто збирайте випадковість з /dev/urandom(можливо, /dev/randomзамість цього потрібно використовувати криптографічно сильний генератор псевдовипадкових чисел, і якщо у вас є багато часу, або ще можливо апаратний генератор випадкових чисел), видаліть кожен символ, який не є десятковою цифрою, складіть вихід на довжину $MAXі вирізати ведучі 0. Якщо у нас трапилось лише 0, то $rndце порожнє, тож у цьому випадку встановлено rndзначення 0. Перевірте, чи є результат поза нашим діапазоном, а якщо так, то повторіть. Я змусив "тіло" циклу в той час, коли він охороняється, щоб принаймні один раз примусити виконати тіло в дусі емуляції do ... whileциклу, оскільки для цього rndне визначено.

Я думаю, що я виконав умови 1 і 2 тут, але зараз я накрутив умову 3. Це ніби повільно. Займає до секунди або близько того (десяту частину секунди, коли мені пощастить). Насправді цикл навіть не гарантовано припиняється (хоча ймовірність припинення зближується до 1 із збільшенням часу).

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

Таблиця відповідей

  1. Найбільш основна (а отже, і портативна) ідея - генерувати випадкову бітструмент досить довго. Існують різні способи генерації випадкової бітстрингу, або за допомогою вбудованої $RANDOMзмінної bash, або за допомогою odі /dev/urandom(або /dev/random). Якщо випадкове число більше $MAX, почніть з початку.

  2. Як варіант, можливе використання зовнішніх інструментів.

    • Рішення Perl
      • Pro: досить портативний, простий, гнучкий
      • Контраст: не для дуже великих чисел вище 2 32 -1
    • Рішення Python
      • Pro: простий, гнучкий, працює навіть для великої кількості
      • Контраст: менш портативний
    • Рішення zsh
      • Pro: добре для людей, які в будь-якому випадку використовують zsh
      • Контраст: напевно, навіть менш портативний

Чому слід вибирати лише цілі числа, а не кодування base64 випадковими бітами, а потім перетворення певної кількості символів (залежно від необхідного діапазону) з закодованої форми в base10 з base64?
муру

Чи потрібно це басити? Щось rand=$(command)робити, якби commandповернути ітератор, який відповідає вашим вимогам?
terdon

@muru Насправді це приємна ідея. Я витратив певну думку на подібну ідею, використовуючи dd if=/dev/urandom 2>/dev/nullта прокладаючи це через od -t d(уникає об'їзду через base64), але мені незрозуміло, як відбувається перетворення та чи справді воно є неупередженим. Якщо ви зможете розширити свою ідею до ефективного, працюючого сценарію та пояснити, чому немає упередженості, це дозволило б отримати чудову відповідь. :)
Malte Skoruppa

@terdon Я вважаю за краще баш. Я маю на увазі, звичайно, ви можете посилатися на pythonабо perlулюблену мову, але це не завжди доступне. Я вважаю за краще щось більш портативне. Ну, awkвипадкова функція була б добре, я думаю. Але чим портативніше, тим краще :)
Malte Skoruppa

2
Так, я думав так, як perl -e 'print int(rand(2**32-1))');. Це досить чортово портативно і буде дуже швидко. Awk не вирізає це, оскільки більшість реалізацій починається з того самого насіння. Таким чином, ви отримуєте однакове випадкове число на наступних прогонах. Він змінюється лише в межах одного циклу.
terdon

Відповіді:


17

Я бачу звідси ще один цікавий метод .

rand=$(openssl rand 4 | od -DAn)

Цей теж здається хорошим варіантом. Він зчитує 4 байти з випадкового пристрою і форматує їх як непідписане ціле число між 0та 2^32-1.

rand=$(od -N 4 -t uL -An /dev/urandom | tr -d " ")


чому odкоманди різні. Обидва просто друкують 4-байтові цілі числа, не підписані: 1-е - з openssl, 2-е - з /dev/random.
jfs

1
@Ramesh я відредагував використовувати /dev/urandomзамість /dev/random- я не бачу причин для використання /dev/random, і це може бути дуже дорого / повільно або сповільнювати інші частини системи. (Не соромтеся редагувати назад і поясніть, чи дійсно це потрібно.)
Volker Siegel,

1
Не хвилюйтесь, насправді дивно, що ця проста різниця має настільки складні наслідки. Ось чому я наполягав змінити приклад на правильний - люди вчаться на прикладах.
Волкер Зігель

1
@MalteSkoruppa: Iозначає , sizeof(int)що може бути менше , ніж 4в принципі. btw, od -DAnне працює, (2**32-1)але od -N4 -tu4 -Anпродовжує працювати.
jfs

8

Дякую всім за всі ваші чудові відповіді. Я закінчив таке рішення, яким я хотів би поділитися.

Перш ніж розібратися в деталях про те, про що і як, ось ось tl; dr : мій новий блискучий сценарій :-)

#!/usr/bin/env bash
#
# Generates a random integer in a given range

# computes the ceiling of log2
# i.e., for parameter x returns the lowest integer l such that 2**l >= x
log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

# uses $RANDOM to generate an n-bit random bitstring uniformly at random
#  (if we assume $RANDOM is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 60 bits
get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

# alternative implementation of get_n_rand_bits:
# uses /dev/urandom to generate an n-bit random bitstring uniformly at random
#  (if we assume /dev/urandom is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 56 bits
get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

# for parameter max, generates an integer in the range {0..max} uniformly at random
# max can be an arbitrary integer, needs not be a power of 2
rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

# MAIN SCRIPT

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

# need absolute value of diff since min (and also max) may be negative
diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

Збережіть це, ~/bin/randі у вас є доступна солодка випадкова функція в bash, яка може вибирати ціле число в заданому довільному діапазоні. Діапазон може містити від’ємні та додатні цілі числа та може бути до 2 60 -1 у довжину:

$ rand 
Usage: rand [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
$ rand 1 10
9
$ rand -43543 -124
-15757
$ rand -3 3
1
$ for i in {0..9}; do rand $((2**60-1)); done
777148045699177620
456074454250332606
95080022501817128
993412753202315192
527158971491831964
336543936737015986
1034537273675883580
127413814010621078
758532158881427336
924637728863691573

Усі ідеї інших відповідачів були чудовими. Відповіді тердона , Дж. Ф. Себастьяна та Джімія використовували зовнішні інструменти, щоб виконати завдання просто та ефективно. Однак я віддав перевагу справжньому баш-рішенню для максимальної портативності, а може, і трохи, просто з любові до bash;)

Відповіді Рамеша та l0b0 використовуються /dev/urandomабо /dev/randomв поєднанні з od. Це добре, однак, їхні підходи мали недолік лише в тому, що вони могли вибирати випадкові цілі числа в діапазоні від 0 до 2 8n -1 для деякого n, оскільки цей метод вибірки байтів, тобто біт-рядки довжиною 8. Це досить великі стрибки з збільшуючи n.

Нарешті, відповідь Фалько описує загальну думку про те, як це можна зробити для довільних діапазонів (не тільки потужностей двох). В основному, для заданого діапазону {0..max}ми можемо визначити, яка наступна потужність двох, тобто, скільки саме бітів потрібно представити maxяк бітструмент. Тоді ми можемо відібрати лише стільки бітів і побачити, чи більша ця бістринга як ціле число max. Якщо так, повторіть. Оскільки ми відбираємо стільки бітів, скільки потрібно для представлення max, кожна ітерація має ймовірність, що більша або дорівнює 50% успіху (50% у гіршому випадку, 100% у кращому випадку). Тож це дуже ефективно.

Мій сценарій - це в основному конкретна реалізація відповіді Фалько, написаного в чистому стилі та високоефективний, оскільки він використовує вбудовані побітові операції bash для вибірки бітстригів потрібної довжини. Крім того, він шанує ідею Елія Кагана, яка пропонує використовувати вбудовану $RANDOMзмінну шляхом зменшення бітстрингу, що виникає внаслідок повторних викликів $RANDOM. Я реально реалізував і можливості використання, /dev/urandomі $RANDOM. За замовчуванням вищевказаний сценарій використовує $RANDOM. (І добре, якщо для використання /dev/urandomнам потрібні od і tr , але вони підтримуються POSIX.)

То як це працює?

Перш ніж я зайнятися цим, два спостереження:

  1. Виявляється, bash не може обробляти цілі числа, більші за 2 63 -1. Побачте самі:

    $ echo $((2**63-1))
    9223372036854775807
    $ echo $((2**63))
    -9223372036854775808

    Здається, що bash внутрішньо використовує підписані 64-бітні цілі числа для зберігання цілих чисел. Отже, у 2 63 воно «обертається» і ми отримуємо від’ємне ціле число. Тож ми не можемо сподіватися отримати будь-який діапазон більше 2 63 -1 із будь-якою випадковою функцією. Bash просто не впорається з цим.

  2. Щоразу, коли ми хочемо відібрати значення в довільному діапазоні між minі, maxможливо min != 0, ми можемо просто відібрати значення між 0і max-minзамість цього, а потім додати minдо кінцевого результату. Це працює, навіть якщо, minможливо, також maxє негативними , але нам потрібно бути обережними для вибірки значення між 0і абсолютним значенням max-min . Тоді ми можемо зосередитись на тому, як відібрати вибіркове значення між 0та довільним натуральним числом max. Решта легко.

Крок 1. Визначте, скільки бітів потрібно для представлення цілого числа (логарифм)

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

Подивимось. Оскільки з nбітами ми можемо представляти до значення 2 n -1, то кількість nбітів, необхідних для представлення довільного значення, x- це стеля (log 2 (x + 1)). Отже, нам потрібна функція для обчислення стелі логарифму до основи 2. Це досить зрозуміло:

log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

Умова нам потрібна, n>0тому якщо вона зростає занадто великою, загортається і стає негативною, цикл гарантовано припиняється.

Крок 2: Вибірка вибіркової довжини шматочка довжини n

Найбільш портативні ідеї - або використовувати /dev/urandom(або навіть /dev/randomякщо є вагомі причини), або вбудовувати $RANDOMзмінну bash . Давайте розглянемо, як це зробити $RANDOMспочатку.

Варіант A: Використання $RANDOM

Для цього використовується ідея, згадана Елією Каган. В основному, оскільки $RANDOMвибірки є 15-бітним цілим числом, ми можемо використовувати $((RANDOM<<15|RANDOM))для вибірки 30-бітного цілого числа. Це означає, що змініть перший виклик $RANDOMна 15 біт ліворуч і застосуйте побіжно або з другим викликом $RANDOM, ефективно змикаючи два незалежно відібраних бітстринга (або, принаймні, настільки ж незалежні, як йде вбудована версія bash $RANDOM).

Ми можемо повторити це, щоб отримати 45-бітне або 60-бітове ціле число. Після цього баш вже не може впоратися з цим, але це означає, що ми можемо легко відібрати випадкове значення між 0 і 2 60 -1. Отже, для вибірки n-бітового цілого числа ми повторюємо процедуру, поки наш випадковий біт-рядок, довжина якого зростає кроками 15 біт, не має довжини, більшої або дорівнює n. Нарешті, ми вирізаємо занадто багато бітів шляхом відповідного побітного зміщення вправо і закінчуємо n-бітним випадковим цілим числом.

get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

Варіант В: Використання /dev/urandom

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

Найменша кількість байтів, необхідна для отримання принаймні n біт, - найменше кратне значення 8, яке більше або рівне n, тобто підлогу ((n + 7) / 8).

Це працює лише до 56-бітових цілих чисел. Вибірка ще одного байта отримає нам 64-бітове ціле число, тобто значення до 2 64 -1, з яким bash не може впоратися.

get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

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

nЗараз ми можемо відібрати біт-рядки, але ми хочемо відібрати цілі числа в діапазоні від 0до max, рівномірно , рівномірно , де maxможе бути довільне, а не обов'язково два. (Ми не можемо використовувати модуль, оскільки це створює зміщення.)

Вся суть, чому ми так наполегливо намагалися відібрати стільки бітів, скільки потрібно для представлення значення max, - це те, що тепер ми можемо безпечно (і ефективно) використовувати цикл для повторної вибірки nбітового біта, поки не відіб'ємо значення, яке є нижчим. або дорівнює max. У гіршому випадку ( maxце потужність у два), кожна ітерація закінчується з вірогідністю 50%, а в кращому випадку ( maxце сила двох мінус одна), перша ітерація закінчується напевно.

rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

Згортання речей

Нарешті, ми хочемо вибірки цілих чисел між minі max, де minі maxможуть бути довільними, навіть від’ємними. Як було сказано раніше, це зараз банально.

Давайте покладемо все це на баш сценарій. Зробіть деякі матеріали для аналізу аргументів ... Ми хочемо два аргументи minта max, або лише один аргумент max, де minза замовчуванням 0.

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

... і, нарешті, для вибіркової вибіркової вибірки значення між minі max, ми вибираємо випадкове ціле число між 0і абсолютним значенням max-minта додаємо minдо кінцевого результату. :-)

diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

Натхненний цим , я можу спробувати використати dieharder для тестування та порівняння цього PRNG, і розміщую свої висновки тут. :-)


Ваше рішення передбачає, що sizeof(int) == 8(64bit) через--format=u
jfs

1
ваше рішення нагадує мені, як пишеться random.py. random.Randomклас використовує 53bit? генератор для повернення довільних великих випадкових чисел (кілька викликів), random.SystemRandomробить те саме, os.urandom()що може бути реалізовано за допомогою /dev/urandom.
jfs

uL означає sizeof (long)> = 8 для діапазону. Це не гарантується. Ви можете використовувати u8, щоб стверджувати, що платформа має таке ціле число.
jfs

@JFSebastian Я думав, що поки що мій сценарій не чітко кодує жодних припущень щодо розміру довгого int. Потенційно це може працювати, навіть якщо розмір довго підписаного int був більшим (або меншим), ніж 64 біт, наприклад, 128 біт. Однак, якщо я використовую, --format=u8я жорстко кодую припущення sizeof(int)==8. З іншого боку, при використанні --format=uLнемає жодних проблем: я не думаю, що існує платформа, яка має 64-бітні цілі числа, але все ж визначає довгі ints як щось нижче. Отже, я б, по суті, стверджував, що --format=uLзабезпечує більшу гнучкість Які ваші думки?
Malte Skoruppa

Є те, long longщо на деяких платформах може бути 64-бітовий, а int = long = 32 біт. Ви не повинні претендувати на діапазон 0..2 ** 60, якщо ви не можете гарантувати його на всіх платформах. З іншого боку, bash може не підтримувати цей діапазон сам на таких платформах (я не знаю, можливо, він використовує maxint_t і тоді u8 є більш правильним, якщо ви хочете стверджувати фіксований діапазон ( odне підтримує вказівку maxint, якщо ваш діапазон є незалежно від платформи, залежної від платформи? Діапазон). Якщо діапазон bash залежить від розміру довгий, то uL може бути більш підходящим). Ви хочете повний діапазон, який підтримує bash на всіх ОС, або фіксований діапазон?
jfs

6

Чи може це бути zsh?

max=1000
integer rnd=$(( $(( rand48() )) * $max ))

Ви можете використовувати насіння також rand48(seed). Дивіться man zshmodulesі man 3 erand48для детального опису , якщо цікаво.


Я особисто не використовую zsh, але це чудове доповнення :)
Malte Skoruppa

5
$ python -c 'import random as R; print(R.randint(-3, 5**1234))'

python доступний у системах Redhat, на базі Debian.


+1 Ах, поряд з розчином perl, просто мав бути розчин пітона. Дякую :)
Malte Skoruppa

5

Якщо ви хочете число від 0 до (2 ^ n) -1, де n mod 8 = 0, ви можете просто отримати n / 8 байт /dev/random. Наприклад, для отримання десяткового представлення випадкового intви можете:

od --read-bytes=4 --address-radix=n --format=u4 /dev/random | awk '{print $1}'

Якщо ви хочете взяти лише n біт, ви можете спочатку взяти потолочний (n / 8) байт і перейти вправо до потрібної кількості. Наприклад, якщо потрібно 15 біт:

echo $(($(od --read-bytes=2 --address-radix=n --format=u4 /dev/random | awk '{print $1}') >> 1))

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


Дякую. Отже, отримайте nвипадкові байти з /dev/urandomта форматуйте за допомогою od. Схожий за духом, як ця відповідь . Обидва однаково хороші :) Хоча обидва мають недолік у фіксованому діапазоні від 0 до 2 ^ (n * 8) -1 біт, де n - кількість байтів. Я б віддав перевагу методу довільного діапазону, до 2 ^ 32-1, але і нічого нижчого. Це створює труднощі зміщення.
Malte Skoruppa

Відредагований для використання /dev/urandomзамість /dev/random- я не бачу причин для використання /dev/random, і це може бути дуже дорого / повільно або сповільнювати інші частини системи. (Не соромтеся редагувати назад і поясніть, чи справді це потрібно.)
Volker Siegel,

Це повинно бути прямо навпаки: використовуйте / dev / urandom, якщо ви не знаєте, що вам потрібно / dev / random . Неправильно вважати, що /dev/urandomрезультати настільки гірші, ніж /dev/randomурадон у більшості випадків. Після /dev/urandomініціалізації (при запуску системи); його результати такі ж хороші, як і /dev/randomдля майже всіх програм на Linux. У деяких системах випадкові та урадомі однакові.
jfs

1
--format=uслід замінити тим, --format=u4що sizeof(int)може бути менше, ніж 4теоретично.
jfs

@JFSebastian У цьому документі є дуже цікава дискусія навколо цієї теми. Їх висновок видається таким, що і те, /dev/randomі інше /dev/urandomє незадовільним, і що "Linux повинен додати захищений RNG, який блокує, поки він не зібрає адекватну ентропію насіння і далі поводиться так urandom".
l0b0

3

Якщо ви не заперечуєте проти використання зовнішніх інструментів, це повинно відповідати вашим вимогам:

rand=$(perl -e 'print int(rand(2**32-1))'); 

Він використовує randфункцію perl, яка в якості параметра бере верхню межу. Ви можете встановити все, що завгодно. Наскільки це близьке до справжньої випадковості в абстрактному математичному визначенні виходить за межі цього веб-сайту, але це повинно бути добре, якщо вам це не потрібне для надзвичайно чутливого шифрування тощо. Можливо, навіть там, але я не буду ризикувати думкою.


це перерва для великої кількості, наприклад, 5 ** 1234
jfs

1
@JFSebastian так. Я опублікував це з моменту вказаної ОП, 1^32-1але вам потрібно налаштувати його для більшої кількості.
тердон

2

Ви повинні отримати найближчий (2 ^ X) -1 рівний або терку, ніж бажаний максимум, і отримаєте кількість біт. Тоді просто зателефонуйте / dev / random декілька разів і додайте всі біти разом, поки не вистачить, обрізаючи всі біти, які занадто багато. Якщо отримане число перевищує максимальне повторення. У гіршому випадку у вас більше 50% шансів отримати випадкове число нижче вашого максимального, тому (для цього найгіршого випадку) ви будете приймати два дзвінки в середньому.


Це насправді досить гарна ідея для підвищення ефективності. Відповідь Рамеша і відповідь l0b0 в обох основному отримують випадкові біти з /dev/urandom, але в обох відповідях це завжди кратна 8 біт. Обрізання бітів, яких занадто багато для нижчих діапазонів перед форматуванням у десяткові, od- це хороша ідея для підвищення ефективності, оскільки цикл має лише очікувану кількість 2 ітерацій, як ви добре пояснюєте. Це, в поєднанні з будь-якою із згаданих відповідей, ймовірно, є шляхом.
Malte Skoruppa

0

Ваша відповідь цікава, але досить довга.

Якщо ви хочете довільно великі числа, то ви можете приєднатися до кількох випадкових чисел у помічнику:

# $1 - number of 'digits' of size base
function random_helper()
{
  base=32768
  random=0
  for((i=0; i<$1; ++i)); do
    let "random+=$RANDOM*($base**$i)"
  done
  echo $random
}

Якщо проблема є упередженою, то просто усуньте її.

# $1 - min value wanted
# $2 - max value wanted
function random()
{
  MAX=32767
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$RANDOM
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}

Об’єднання цих функцій разом

# $1 - min value wanted
# $2 - max value wanted
# $3 - number of 'digits' of size base
function random()
{
  base=32768
  MAX=$((base**$3-1))
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$(random_helper)
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.