Реализация rand()
Я пишу некоторый встроенный код на C и мне нужно использовать функцию rand(). К сожалению, rand() не поддерживается в библиотеке для контроллера. Мне нужна простая реализация, которая быстра, но, что еще важнее, не требует много места и дает случайные числа относительно высокого качества. Кто-нибудь знает, какой алгоритм использовать или пример кода?
РЕДАКТИРОВАТЬ: Это для обработки изображений, так что "относительно высокое качество" означает приличную длину цикла и хорошие однородные свойства.
11 ответов
Проверьте эту коллекцию генераторов случайных чисел от Джорджа Марсалья. Он является ведущим экспертом в области генерации случайных чисел, поэтому я был бы уверен, используя все, что он рекомендует. Генераторы в этом списке крошечные, некоторым требуется только пара беззнаковых длин в качестве состояния.
Генераторы Marsaglia определенно "высокого качества" по вашим меркам длительного периода и хорошего равномерного распределения. Они проходят строгие статистические тесты, но не подходят для криптографии.
Используйте код C для LFSR113 от L'écuyer:
unsigned int lfsr113_Bits (void)
{
static unsigned int z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345;
unsigned int b;
b = ((z1 << 6) ^ z1) >> 13;
z1 = ((z1 & 4294967294U) << 18) ^ b;
b = ((z2 << 2) ^ z2) >> 27;
z2 = ((z2 & 4294967288U) << 2) ^ b;
b = ((z3 << 13) ^ z3) >> 21;
z3 = ((z3 & 4294967280U) << 7) ^ b;
b = ((z4 << 3) ^ z4) >> 12;
z4 = ((z4 & 4294967168U) << 13) ^ b;
return (z1 ^ z2 ^ z3 ^ z4);
}
Очень качественно и быстро. НЕ используйте rand() ни для чего. Это хуже, чем бесполезно.
Вот ссылка на реализацию ANSI C нескольких генераторов случайных чисел.
Я сделал коллекцию генераторов случайных чисел " simplerandom", которые компактны и подходят для встроенных систем. Коллекция доступна в C и Python.
Я искал кучу простых и приличных, которые смог найти, и собрал их в небольшой пакет. Они включают в себя несколько генераторов Marsaglia (KISS, MWC, SHR3) и пару генераторов L'Ecuyer LFSR.
Все генераторы возвращают 32-разрядное целое число без знака и обычно имеют состояние от 1 до 4 32-разрядных целых чисел без знака.
Интересно, что я обнаружил несколько проблем с генераторами Marsaglia, и я попытался исправить / улучшить все эти проблемы. Эти вопросы были:
- Генератор SHR3 (компонент генератора KISS Марсальи 1999 года) был сломан.
- Младшие 16 битов MWC имеют период приблизительно 229,1. Таким образом, я сделал немного улучшенный MWC, который дает младшим 16 битам период 259,3, который является общим периодом этого генератора.
Я обнаружил несколько проблем с заполнением и попытался создать надежные процедуры заполнения (инициализации), чтобы они не сломались, если вы дадите им "плохое" начальное значение.
Немного из Википедии:
- Он был разработан, чтобы иметь период 219937 - 1 (создатели алгоритма доказали это свойство). На практике нет особых причин использовать больший период, поскольку для большинства приложений не требуется 219937 уникальных комбинаций (219937 - это приблизительно 4,3 × 106001; это на много порядков больше, чем предполагаемое количество частиц в наблюдаемой вселенной, что составляет 1080).
- Он имеет очень высокий порядок равномерного распределения (см. Линейный конгруэнтный генератор). Это подразумевает, что между последовательными значениями в выходной последовательности существует незначительная последовательная корреляция.
Он проходит многочисленные тесты на статистическую случайность, включая тесты Диарда. Он проходит большинство, но не все, еще более строгих тестов случайности TestU01 Crush.
Исходный код для многих языков доступен по ссылке.
Я рекомендую академическую статью " Две быстрые реализации генератора минимальных стандартных случайных чисел " Дэвида Карта. Вы можете найти бесплатный PDF через Google. Также стоит прочитать оригинальную статью о минимальном стандартном генераторе случайных чисел.
Код Carta дает быстрые, высококачественные случайные числа на 32-битных машинах. Для более тщательной оценки см. Статью.
Я нашел это: Простая генерация случайных чисел, Джон Д. Кук.
Это должно быть легко адаптироваться к C, учитывая, что это всего лишь несколько строк кода.
Изменить: и вы могли бы уточнить, что вы подразумеваете под "относительно высокого качества". Вы генерируете ключи шифрования для ядерных кодов запуска или случайные числа для игры в покер?
Более того, используйте несколько линейных регистров сдвига с обратной связью, объедините их вместе.
При условии, что sizeof(unsigned) == 4
:
unsigned t1 = 0, t2 = 0;
unsigned random()
{
unsigned b;
b = t1 ^ (t1 >> 2) ^ (t1 >> 6) ^ (t1 >> 7);
t1 = (t1 >> 1) | (~b << 31);
b = (t2 << 1) ^ (t2 << 2) ^ (t1 << 3) ^ (t2 << 4);
t2 = (t2 << 1) | (~b >> 31);
return t1 ^ t2;
}
Я бы взял один из библиотеки GNU C, источник доступен для просмотра в Интернете.
http://qa.coreboot.org/docs/libpayload/rand_8c-source.html
Но если у вас есть какие-либо сомнения по поводу качества случайных чисел, вам, вероятно, стоит взглянуть на более тщательно написанные математические библиотеки. Это большой предмет и стандарт rand
реализации не очень высоко оценены экспертами.
Вот еще одна возможность: http://www.boost.org/doc/libs/1_39_0/libs/random/index.html
(Если вы обнаружите, что у вас слишком много вариантов, вы всегда можете выбрать один наугад.)
Существует один простой RNG с именем KISS, это один генератор случайных чисел по трем числам.
/* Implementation of a 32-bit KISS generator which uses no multiply instructions */
static unsigned int x=123456789,y=234567891,z=345678912,w=456789123,c=0;
unsigned int JKISS32() {
int t;
y ^= (y<<5); y ^= (y>>7); y ^= (y<<22);
t = z+w+c; z = w; c = t < 0; w = t&2147483647;
x += 1411392427;
return x + y + w;
}
Также есть один веб-сайт для тестирования ГСЧ http://www.phy.duke.edu/~rgb/General/dieharder.php
Стандартное решение - использовать сдвиговый регистр с линейной обратной связью.