Функция Unix $RANDOM не случайна в терминале Bash

Я пытаюсь сгенерировать случайное число между указанным диапазоном с помощью функции $RANDOM в терминале bash. Проблема в том, что числа, которые он генерирует, вообще не кажутся случайными. Сценарий, который я использую:

RANDOM=$$;

a=$RANDOM
b=9; #Number of scripts in collection -1
c=`expr $a % $b `; #Random number between 0 and b

if (( c==0 ));
then

echo "script 1 (random = $a, mod = $c)";

elif (( c==1 ));
then

echo "stript 2 (random = $a, mod = $c)";

...

else

echo "error (random = $a, mod = $c)";

fi;

Если я запускаю это в цикле for, я получаю, например:

script 8 (random = 17845, mod = 7)
script 8 (random = 18754, mod = 7)
script 8 (random = 19663, mod = 7)
script 7 (random = 20571, mod = 6)
script 7 (random = 21480, mod = 6)
script 6 (random = 22388, mod = 5)
script 6 (random = 23297, mod = 5)
script 6 (random = 24206, mod = 5)
script 5 (random = 25114, mod = 4)
script 5 (random = 26023, mod = 4)

Что явно не случайно.

Я попытался удалить $ а и просто работает

c=`expr $RANDOM % $b`; 

а затем изменить код в другой вариант

c=`expr $a \* $b \/ 32767`;

Но эти (неудивительно) вернули тот же результат. Что я делаю неправильно? Или это просто немного раздражающее ограничение для $RANDOM? Любой совет будет принята с благодарностью.

2 ответа

Решение

Вы продолжали посылать СЛУЧАЙНО с тем же номером. Попытайтесь не засевать это или посеять более случайный предмет вместо этого:

RANDOM=$$

По-видимому $$ не меняется всегда, так как это всегда основной PID (не PID подоболочки) вашей оболочки. Если вы на самом деле вызываете разные оболочки, то, вероятно, нет большой разницы, поскольку числа, добавляемые каждым PID, увеличиваются только на единицу. Так что либо вы можете удалить это, либо получить другое случайное семя где-то вроде /dev/urandom, так далее.

Один хороший способ применить случайное семя /dev/urandom:

RANDOM=$(tr -dc 0-9 < /dev/urandom | head -c10)

Еще через наносекунды (посев больших чисел, по-видимому, не дает хорошего эффекта):

RANDOM=$(date '+%N')

Также, чтобы он выглядел более уникальным среди различных подпроцессов, добавьте BASHPID (лучше, чем $$) к своему семени:

RANDOM=$(( BASHPID + $(date '+%N') ))

Я думаю, что объяснение можно найти здесь:

Когда вы используете операцию модуля, вы выбираете информацию из младших битов числа и отбрасываете информацию из старших битов... Наименее значимые (правые) цифры X не очень случайны, поэтому решения, основанные на На число X всегда должны влиять наиболее значимые цифры.

И использование этого работает лучше для меня (хотя я тестировал только несколько раз):

c=$(($a * $b / 32768))

Вот пересмотренный скрипт:

#!/bin/bash

RANDOM=$$;
a=$RANDOM

b=9; #Number of scripts in collection -1

c=$(($a * $b / 32768))

if (( c==0 )); then
    echo "script 1 (random = $a, mod = $c)";
elif (( c==1 )); then
    echo "script 2 (random = $a, mod = $c)";
else
    echo "error (random = $a, mod = $c)";
fi;

Надеюсь это поможет.

Другие вопросы по тегам