Есть ли функция для генерации случайного целого числа в C? Или мне придется использовать стороннюю библиотеку?
srand
: зачем звонить только один раз .
Есть ли функция для генерации случайного целого числа в C? Или мне придется использовать стороннюю библиотеку?
srand
: зачем звонить только один раз .
Ответы:
Примечание : не используйте
rand()
для безопасности. Если вам нужен криптографически защищенный номер, посмотрите этот ответ .
#include <time.h>
#include <stdlib.h>
srand(time(NULL)); // Initialization, should only be called once.
int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX.
Изменить : В Linux вы можете предпочесть использовать random и srandom .
time()
меняется только один раз в секунду. Если вы начнете с time()
каждого вызова rand()
, вы получите одинаковое значение для каждого вызова в течение одной секунды. Но главная причина в том, что свойства rand()
и функции, подобные этим, известны лучше всего для случая использования, когда они засеваются ровно один раз за цикл, а не при каждом вызове. Зависимость от «случайности» с непроверенными или недоказанными свойствами приводит к неприятностям.
rand()
обычно и есть), с которым посев с rand()
в лучшем случае не будет иметь никакого эффекта вообще, а в худшем разрушит известные качества генератора. Это глубокий предмет. Начните с чтения Кнута Том 2 Глава 3 о случайных числах как лучшее введение в математику и подводные камни.
srand((unsigned int)time(NULL));
rand()
Функция <stdlib.h>
возвращает псевдо-случайное число между 0 и RAND_MAX
. Вы можете использовать, srand(unsigned int seed)
чтобы установить семя.
Обычной практикой является использование %
оператора в сочетании с rand()
получением другого диапазона (хотя имейте в виду, что это несколько нарушает однородность). Например:
/* random int between 0 and 19 */
int r = rand() % 20;
Если вы действительно заботитесь о единообразии, вы можете сделать что-то вроде этого:
/* Returns an integer in the range [0, n).
*
* Uses rand(), and so is affected-by/affects the same seed.
*/
int randint(int n) {
if ((n - 1) == RAND_MAX) {
return rand();
} else {
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to rand()
assert (n <= RAND_MAX)
// Chop off all of the values that would cause skew...
int end = RAND_MAX / n; // truncate skew
assert (end > 0);
end *= n;
// ... and ignore results from rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ((r = rand()) >= end);
return r % n;
}
}
%
оператор модуля. Это дает вам остаток от целочисленного деления, поэтому x % n
всегда будет давать вам число между 0
и n - 1
(до тех пор, пока x
и n
оба положительны). Если вы все еще находите это сбивающим с толку, попробуйте написать программу, которая имеет i
счет от 0 до 100 и распечатывает i % n
для некоторых n
из вашего выбора меньше 100.
Что касается того, как безопасно генерировать случайные числа на разных языках программирования , вам нужно выполнить одно из следующих действий:
randombytes
API/dev/urandom
, а не /dev/random
. Не OpenSSL (или другие PRNG пользовательского пространства).Например:
#include "sodium.h"
int foo()
{
char myString[32];
uint32_t myInt;
if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */
return 1;
}
/* myString will be an array of 32 random bytes, not null-terminated */
randombytes_buf(myString, 32);
/* myInt will be a random number between 0 and 9 */
myInt = randombytes_uniform(10);
}
randombytes_uniform()
криптографически безопасен и непредвзят.
sodium_init()
в какой-то момент. Не беспокойтесь о RNG, он использует ядро.
sodium_init()
хотя это не обязательно является частью моего примера, потому что это важная деталь.
Давайте пройдем через это. Сначала мы используем функцию srand () для заполнения рандомизатора. По сути, компьютер может генерировать случайные числа на основе числа, которое передается в srand (). Если вы дали одинаковое начальное значение, то каждый раз будут генерироваться одни и те же случайные числа.
Следовательно, мы должны заполнить рандомизатор значением, которое всегда меняется. Мы делаем это, передавая значение текущего времени с помощью функции time ().
Теперь, когда мы вызываем rand (), каждый раз будет создаваться новое случайное число.
#include <stdio.h>
int random_number(int min_num, int max_num);
int main(void)
{
printf("Min : 1 Max : 40 %d\n", random_number(1,40));
printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
return 0;
}
int random_number(int min_num, int max_num)
{
int result = 0, low_num = 0, hi_num = 0;
if (min_num < max_num)
{
low_num = min_num;
hi_num = max_num + 1; // include max_num in output
} else {
low_num = max_num + 1; // include max_num in output
hi_num = min_num;
}
srand(time(NULL));
result = (rand() % (hi_num - low_num)) + low_num;
return result;
}
else{ low_num=max_num; hi_num=min_num+1;
2) не удается, когда hi_num - low_num > INT_MAX
. 3) Опускает значения в редких ситуациях INT_MAX > hi_num - low_num > RAND_MAX
.
hi_num = max_num + 1;
отсутствует защита от переполнения.
Если вам нужны более качественные псевдослучайные числа, чем те, которые stdlib
есть, посмотрите Mersenne Twister . Это тоже быстрее. Примеры реализации многочисленны, например, здесь .
Стандартная функция C есть rand()
. Это достаточно хорошо, чтобы раздавать карты для пасьянса, но это ужасно. Многие реализации rand()
циклически проходят через короткий список чисел, а младшие биты имеют более короткие циклы. Путь, который вызывают некоторые программы, rand()
ужасен, а рассчитать хорошее начальное число для передачи srand()
довольно сложно.
Лучший способ генерировать случайные числа в C - это использовать стороннюю библиотеку, такую как OpenSSL. Например,
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>
/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
union {
unsigned int i;
unsigned char c[sizeof(unsigned int)];
} u;
do {
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
} while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
return u.i % limit;
}
/* Random double in [0.0, 1.0) */
double random_double() {
union {
uint64_t i;
unsigned char c[sizeof(uint64_t)];
} u;
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
/* 53 bits / 2**53 */
return (u.i >> 11) * (1.0/9007199254740992.0);
}
int main() {
printf("Dice: %d\n", (int)(random_uint(6) + 1));
printf("Double: %f\n", random_double());
return 0;
}
Почему так много кода? Другие языки, такие как Java и Ruby, имеют функции для случайных целых чисел или чисел с плавающей точкой. OpenSSL дает только случайные байты, поэтому я пытаюсь имитировать, как Java или Ruby преобразуют их в целые числа или числа с плавающей точкой.
Для целых чисел мы хотим избежать смещения по модулю . Предположим, что мы получили случайные 4-значные целые числа rand() % 10000
, но rand()
можем вернуть только от 0 до 32767 (как это происходит в Microsoft Windows). Каждое число от 0 до 2767 будет появляться чаще, чем каждое число от 2768 до 9999. Чтобы устранить смещение, мы можем повторить, rand()
пока значение меньше 2768, потому что 30000 значений от 2768 до 32767 отображаются равномерно на 10000 значений от 0 до 9999.
Для чисел с плавающей запятой мы хотим 53 случайных бита, потому что a double
содержит 53 бита точности (при условии, что это IEEE double). Если мы используем более 53 бит, мы получаем смещение округления. Некоторые программисты пишут подобный код rand() / (double)RAND_MAX
, но rand()
могут возвращать только 31 бит или только 15 бит в Windows.
OpenSSL берет свое RAND_bytes()
начало, возможно, читая /dev/urandom
в Linux. Если нам нужно много случайных чисел, было бы слишком медленно читать их все /dev/urandom
, потому что они должны быть скопированы из ядра. Это позволяет OpenSSL генерировать больше случайных чисел из начального числа.
Подробнее о случайных числах:
srand()
. Он смешивает биты из текущего времени, идентификатора процесса и некоторых указателей, если он не может прочитать /dev/urandom
.float
/ double
, поэтому я уточнил вопрос, чтобы придерживаться int
цифр, чтобы не сделать его слишком широким. Существуют и другие вопросы C, касающиеся конкретно float
/ double
случайных значений, поэтому вы можете отправить свою вторую половину ответа на такие вопросы, как stackoverflow.com/questions/13408990/…
Если ваша система поддерживает arc4random
семейство функций, я бы рекомендовал использовать их вместо стандартной rand
функции.
arc4random
Семейство включает в себя:
uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)
arc4random
возвращает случайное 32-разрядное целое число без знака.
arc4random_buf
помещает случайный контент в свой параметр buf : void *
. Количество контента определяетсяbytes : size_t
параметром.
arc4random_uniform
возвращает случайное 32-разрядное целое число без знака, которое следует правилу:, 0 <= arc4random_uniform(limit) < limit
где предел также является 32-разрядным целым числом без знака.
arc4random_stir
считывает данные /dev/urandom
и передает их arc4random_addrandom
для дополнительной рандомизации своего внутреннего пула случайных чисел.
arc4random_addrandom
используется arc4random_stir
для заполнения внутреннего пула случайных чисел в соответствии с переданными ему данными.
Если у вас нет этих функций, но вы работаете в Unix, вы можете использовать этот код:
/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */
int urandom_fd = -2;
void urandom_init() {
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd == -1) {
int errsv = urandom_fd;
printf("Error opening [/dev/urandom]: %i\n", errsv);
exit(1);
}
}
unsigned long urandom() {
unsigned long buf_impl;
unsigned long *buf = &buf_impl;
if (urandom_fd == -2) {
urandom_init();
}
/* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
read(urandom_fd, buf, sizeof(long));
return buf_impl;
}
urandom_init
Функция открывает /dev/urandom
устройство, и помещает дескриптор файла в urandom_fd
.
urandom
Функция в основном такая же , как призыв к rand
, за исключением более безопасным, и он возвращает long
(легко изменяемый).
Однако /dev/urandom
может быть немного медленным, поэтому рекомендуется использовать его в качестве начального числа для другого генератора случайных чисел.
Если ваша система не имеет /dev/urandom
, но действительно есть /dev/random
или подобный файл, то вы можете просто изменить путь , пройденный к open
в urandom_init
. Вызовы и API , используемые в urandom_init
и urandom
являются (я считаю) POSIX-совместимые, и , как таковые, должны работать на большинстве, если не во все совместимые системы POSIX.
Примечания: Чтение из /dev/urandom
НЕ будет блокироваться, если энтропии недостаточно, поэтому значения, сгенерированные в таких обстоятельствах, могут быть криптографически небезопасными. Если вы беспокоитесь об этом, то используйте /dev/random
, который всегда будет блокировать, если энтропии недостаточно.
Если вы работаете в другой системе (например, в Windows), то используйте rand
или какой-нибудь внутренний зависящий от платформы непереносимый API.
Обертка функции для urandom
, rand
или arc4random
вызовов:
#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */
int myRandom(int bottom, int top){
return (RAND_IMPL() % (top - bottom)) + bottom;
}
STL не существует для C. Вы должны позвонить rand
, или еще лучше random
. Они объявлены в заголовке стандартной библиотеки stdlib.h
. rand
это POSIX, random
это функция спецификации BSD.
Разница между rand
и в random
том, что random
возвращает гораздо более пригодное для использования 32-разрядное случайное число и rand
обычно возвращает 16-разрядное число. Страницы руководства BSD показывают, что младшие биты rand
являются циклическими и предсказуемыми, поэтому rand
потенциально бесполезны для небольших чисел.
extern int rand(void);
и extern void srand(unsigned int);
.
Посмотрите на ISAAC (Indirection, Shift, Accumulate, Add и Count). Он равномерно распределен и имеет среднюю продолжительность цикла 2 ^ 8295.
Это хороший способ получить случайное число между двумя числами по вашему выбору.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define randnum(min, max) \
((rand() % (int)(((max) + 1) - (min))) + (min))
int main()
{
srand(time(NULL));
printf("%d\n", randnum(1, 70));
}
Выходной первый раз: 39
Выходной второй раз: 61
Выходной третий раз: 65
Вы можете изменить значения после randnum
на любое число, которое вы выберете, и оно сгенерирует для вас случайное число между этими двумя числами.
Вы хотите использовать rand()
. Примечание ( ОЧЕНЬ ВАЖНО ): обязательно установите начальное значение для функции ранда. Если вы этого не сделаете, ваши случайные числа не являются действительно случайными . Это очень, очень, очень важно. К счастью, вы можете использовать некоторую комбинацию таймера системных тиков и даты, чтобы получить хорошее начальное число.
FWIW, ответ в том, что да, есть stdlib.h
функция называется rand
; эта функция настроена в первую очередь на скорость и распределение, а не на непредсказуемость. Почти все встроенные случайные функции для различных языков и сред используют эту функцию по умолчанию. Существуют также «криптографические» генераторы случайных чисел, которые гораздо менее предсказуемы, но работают намного медленнее. Их следует использовать в любых приложениях, связанных с безопасностью.
Надеюсь, это немного более случайно, чем просто использование srand(time(NULL))
.
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
srand(rand());
for (int i = 0; i < 10; i++)
printf("%d\n", rand());
}
Ну, STL - это C ++, а не C, поэтому я не знаю, что вы хотите. Если вы хотите C, однако, есть rand()
и srand()
функции:
int rand(void);
void srand(unsigned seed);
Они оба являются частью ANSI C. Существует также random()
функция:
long random(void);
Но, насколько я могу судить, random()
это не стандарт ANSI C. Сторонняя библиотека может быть неплохой идеей, но все зависит от того, насколько случайным образом число вам действительно нужно сгенерировать.
C Программа для генерации случайного числа от 9 до 50
#include <time.h>
#include <stdlib.h>
int main()
{
srand(time(NULL));
int lowerLimit = 10, upperLimit = 50;
int r = lowerLimit + rand() % (upperLimit - lowerLimit);
printf("%d", r);
}
В общем, мы можем генерировать случайное число между lowerLimit и upperLimit-1
то есть lowerLimit включительно или, скажем, r ∈ [lowerLimit, upperLimit)
rand()
это наиболее удобный способ генерации случайных чисел.
Вы также можете поймать случайное число из любого онлайн-сервиса, например random.org.
#include <stdio.h>
#include <stdlib.h>
void main()
{
int visited[100];
int randValue, a, b, vindex = 0;
randValue = (rand() % 100) + 1;
while (vindex < 100) {
for (b = 0; b < vindex; b++) {
if (visited[b] == randValue) {
randValue = (rand() % 100) + 1;
b = 0;
}
}
visited[vindex++] = randValue;
}
for (a = 0; a < 100; a++)
printf("%d ", visited[a]);
}
#include <stdio.h>
#include <dos.h>
int random(int range);
int main(void)
{
printf("%d", random(10));
return 0;
}
int random(int range)
{
struct time t;
int r;
gettime(&t);
r = t.ti_sec % range;
return r;
}
На современных процессорах x86_64 вы можете использовать аппаратный генератор случайных чисел через _rdrand64_step()
Пример кода:
#include <immintrin.h>
uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
// Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//generate number in range [min,max)
int random(int min, int max){
int number = min + rand() % (max - min);
return number;
}
//Driver code
int main(){
srand(time(NULL));
for(int i = 1; i <= 10; i++){
printf("%d\t", random(10, 100));
}
return 0;
}
Услышав хорошее объяснение того, почему использование rand()
для получения равномерно распределенных случайных чисел в заданном диапазоне является плохой идеей, я решил взглянуть на то, насколько искаженным является результат. Мой тестовый пример был честным бросанием игральных костей. Вот код C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
int i;
int dice[6];
for (i = 0; i < 6; i++)
dice[i] = 0;
srand(time(NULL));
const int TOTAL = 10000000;
for (i = 0; i < TOTAL; i++)
dice[(rand() % 6)] += 1;
double pers = 0.0, tpers = 0.0;
for (i = 0; i < 6; i++) {
pers = (dice[i] * 100.0) / TOTAL;
printf("\t%1d %5.2f%%\n", dice[i], pers);
tpers += pers;
}
printf("\ttotal: %6.2f%%\n", tpers);
}
и вот его вывод:
$ gcc -o t3 t3.c
$ ./t3
1666598 16.67%
1668630 16.69%
1667682 16.68%
1666049 16.66%
1665948 16.66%
1665093 16.65%
total: 100.00%
$ ./t3
1667634 16.68%
1665914 16.66%
1665542 16.66%
1667828 16.68%
1663649 16.64%
1669433 16.69%
total: 100.00%
Я не знаю, насколько вам нужны ваши случайные числа, но вышеприведенное выглядит достаточно равномерным для большинства нужд.
Изменить: было бы неплохо инициализировать PRNG с чем-то лучше, чем time(NULL)
.
У меня была серьезная проблема с генератором псевдослучайных чисел в моем недавнем приложении: я неоднократно вызывал свою программу на C через скрипт pyhton, и я использовал в качестве семени следующий код:
srand(time(NULL))
Тем не менее, так как:
man srand
);time
будет возвращать одно и то же значение каждый раз.Моя программа генерировала ту же последовательность чисел. Вы можете сделать 3 вещи, чтобы решить эту проблему:
смешать вывод времени с некоторой другой информацией, изменяющейся при запуске (в моем приложении имя вывода):
srand(time(NULL) | getHashOfString(outputName))
Я использовал djb2 в качестве хэш-функции.
Увеличьте разрешение по времени. На моей платформе clock_gettime
был доступен, поэтому я использую его:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec);
Используйте оба метода вместе:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec | getHashOfString(outputName));
Вариант 3 гарантирует вам (насколько я знаю) лучшую начальную случайность, но это может создать разницу только в очень быстром приложении. На мой взгляд, вариант 2 - безопасная ставка.
rand()
не должен использоваться для криптографических данных, я согласен. По крайней мере, для меня в моем приложении не было криптографических данных, поэтому для меня это был нормальный метод.
Несмотря на все предложения людей rand()
здесь, вы не хотите использовать, rand()
если вам не нужно! Случайные числа, которые rand()
производят, часто очень плохие. Чтобы процитировать со страницы руководства Linux:
Версии
rand()
иsrand()
в библиотеке Linux C используют тот же генератор случайных чисел, чтоrandom(3)
иsrandom(3)
, поэтому биты младшего разряда должны быть такими же случайными, как биты старшего разряда. Однако в более старых реализациях rand () и в текущих реализациях в разных системах биты младшего разряда гораздо менее случайны, чем биты старшего порядка . Не используйте эту функцию в приложениях, предназначенных для переноса, когда необходима хорошая случайность. ( Используйтеrandom(3)
вместо. )
Относительно переносимости, random()
также определяется стандартом POSIX уже довольно давно. rand()
старше, он появился уже в первой спецификации POSIX.1 (IEEE Std 1003.1-1988), тогда как random()
впервые появился в POSIX.1-2001 (IEEE Std 1003.1-2001), но текущий стандарт POSIX уже POSIX.1-2008 (IEEE Std 1003.1-2008), который получил обновление всего год назад (IEEE Std 1003.1-2008, издание 2016 г.). Так что я бы рассмотрелrandom()
очень портативным.
POSIX.1-2001 также представил lrand48()
и mrand48()
функции, смотрите здесь :
Это семейство функций должно генерировать псевдослучайные числа с использованием линейного конгруэнтного алгоритма и 48-разрядной целочисленной арифметики.
И довольно хороший псевдослучайный источник - это arc4random()
функция, которая доступна во многих системах. Не является частью какого-либо официального стандарта, появившегося в BSD в 1997 году, но вы можете найти его в таких системах, как Linux и macOS / iOS.
random()
не существует в Windows.
rand()
поскольку это также требуется стандартом C. Для всего остального вам нужно специальное решение только для Windows, как обычно. #ifdef _WIN32
Это фраза, которую вы будете чаще всего видеть в кроссплатформенном коде, который хочет поддерживать Windows, а также обычно есть одно решение, которое работает со всеми системами, и то, которое требуется только для Windows.
Для приложений Linux C:
Это мой переработанный код из приведенного выше ответа, который следует моим практикам кода C и возвращает случайный буфер любого размера (с правильными кодами возврата и т. Д.). Обязательно позвоните urandom_open()
один раз в начале вашей программы.
int gUrandomFd = -1;
int urandom_open(void)
{
if (gUrandomFd == -1) {
gUrandomFd = open("/dev/urandom", O_RDONLY);
}
if (gUrandomFd == -1) {
fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
errno, strerror(errno));
return -1;
} else {
return 0;
}
}
void urandom_close(void)
{
close(gUrandomFd);
gUrandomFd = -1;
}
//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
int ret = 0; // Return value
if (gUrandomFd == -1) {
fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
return -1;
}
ret = read(gUrandomFd, buf, size);
if (ret != size) {
fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
ret, size);
return -1;
} else {
return 0;
}
}
Мое минималистичное решение должно работать для случайных чисел в диапазоне [min, max)
. Используйте srand(time(NULL))
перед вызовом функции.
int range_rand(int min_num, int max_num) {
if (min_num >= max_num) {
fprintf(stderr, "min_num is greater or equal than max_num!\n");
}
return min_num + (rand() % (max_num - min_num));
}
Попробуйте это, я собрал это из некоторых концепций, уже упомянутых выше:
/*
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
*/
int random(int max) {
srand((unsigned) time(NULL));
return (rand() % max) + 1;
}
srand()
каждый раз, когда вы хотите позвонить rand()
, это ужасная идея. Поскольку time()
обычно возвращает значение в секундах, быстрый вызов этой функции вернет то же «случайное» значение.
random()
функцией Unix .