Как сгенерировать случайный int в C?

Есть ли функция для генерации случайного целого числа в C? Или мне придется использовать стороннюю библиотеку?

Ответы

Ответ 1

  Примечание: не используйте 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.

Ответ 2

Функция 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;
  }
}

Ответ 3

Если вам нужны безопасные случайные символы или целые числа:

Как описано в , как безопасно генерировать случайные числа на разных языках программирования, вам нужно выполнить одно из следующих действий:

Например:

#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() криптографически безопасен и беспристрастен.

Ответ 4

Давайте рассмотрим это. Сначала мы используем функцию 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;
}

Ответ 5

Если вам нужны более качественные псевдослучайные числа, чем предлагает stdlib, посмотрите Mersenne Twister. Это тоже быстрее. Примеры реализации многочисленны, например здесь.

Ответ 6

Стандартная функция 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 преобразуют их в целые числа или float.

Для целых чисел мы хотим избежать модульного смещения. Предположим, что у нас есть некоторые случайные 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). Если мы используем более 53 бит, мы получаем округление. Некоторые программисты пишут код типа rand() / (double)RAND_MAX, но rand() может вернуть только 31 бит или всего 15 бит в Windows.

OpenSSL RAND_bytes() сам по себе, возможно, прочитав /dev/urandom в Linux. Если нам нужно много случайных чисел, было бы слишком медленно читать их все из /dev/urandom, потому что они должны быть скопированы из ядра. Быстрее разрешить OpenSSL генерировать более случайные числа из семени.

Подробнее о случайных числах:

  • Perl Perl_seed() - пример вычисления семени в C для srand(). Он смешивает биты с текущего времени, идентификатора процесса и некоторых указателей, если он не может читать /dev/urandom.
  • OpenBSD arc4random_uniform() объясняет смещение по модулю.
  • Java API для java.util.Random описывает алгоритмы устранения смещения от случайных целых чисел и упаковки 53 бит в случайные поплавки.

Ответ 7

Если ваша система поддерживает 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 или какой-либо другой специфический для Windows не зависящий от платформы 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;
}

Ответ 8

STL не существует для C. Вы должны называть rand или, еще лучше, random. Они объявлены в стандартном заголовке библиотеки stdlib.h. rand - POSIX, random - функция спецификации BSD.

Разница между rand и random заключается в том, что random возвращает гораздо более удобное 32-битное случайное число, а rand обычно возвращает 16-разрядное число. Команды BSD показывают, что младшие бит rand являются циклическими и предсказуемыми, поэтому rand потенциально бесполезен для небольших чисел.

Ответ 9

Посмотрите ISAAC (Направление, Сдвиг, Накопление, Добавить и Счет). Он равномерно распределен и имеет среднюю длину цикла 2 ^ 8295.

Ответ 10

Вы хотите использовать rand(). Примечание (ОЧЕНЬ ВАЖНО): убедитесь, что вы выбрали семя для функции rand. Если вы этого не сделаете, ваши случайные числа не будут действительно случайными. Это очень, очень, очень важно. К счастью, вы обычно можете использовать некоторую комбинацию таймера тиков системы и дату получения хорошего семени.

Ответ 11

FWIW, ответ заключается в том, что да, есть функция stdlib.h, называемая rand; эта функция настраивается в первую очередь для скорости и распределения, а не для непредсказуемости. Почти все встроенные случайные функции для разных языков и фреймворков используют эту функцию по умолчанию. Существуют также "криптографические" генераторы случайных чисел, которые намного менее предсказуемы, но работают намного медленнее. Они должны использоваться в любом виде приложения, связанного с безопасностью.

Ответ 12

Это хороший способ получить случайное число между двумя номерами по вашему выбору.

#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 на любые номера, которые вы выберете, и он будет генерировать случайное число для вас между этими двумя номерами.

Ответ 13

Это, надеюсь, немного более случайное, чем просто использование 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());
}

Ответ 14

Ну, STL - это С++, а не C, поэтому я не знаю, чего вы хотите. Однако, если вы хотите C, есть функции rand() и srand():

int rand(void);

void srand(unsigned seed);

Они являются частью ANSI C. Существует также функция random():

long random(void);

Но, насколько я могу судить, random() не является стандартным ANSI C. Сторонняя библиотека может быть плохой идеей, но все зависит от того, насколько случайным является число, которое вам действительно нужно создать.

Ответ 15

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

i.e lowerLimit включен или говорит r ∈ [lowerLimit, upperLimit)

Ответ 16

rand() - наиболее удобный способ генерации случайных чисел.

Вы также можете поймать случайное число из любого онлайн-сервиса, такого как random.org.

Ответ 17

#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]);
}

Ответ 18

#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;
}

Ответ 19

На современных процессорах 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

Ответ 20

#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;
}

Ответ 21

Услышав хорошее объяснение того, почему использование 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).

Ответ 22

У меня была серьезная проблема с генератором псевдослучайных чисел в моем недавнем приложении: я повторял свою программу на C через pyhton script, и я использовал в качестве семени следующий код:

srand(time(NULL))

Однако, поскольку:

  • rand будет генерировать ту же псевдослучайную последовательность, которая дает одно и то же семя в srand (см. 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 - безопасная ставка.

Ответ 23

Несмотря на все предложения людей 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 Edition). Поэтому я считаю, что random() очень портативен.

POSIX.1-2001 также представил функции lrand48() и mrand48(), см. здесь:

Это семейство функций должно генерировать псевдослучайные числа, используя линейный конгруэнтный алгоритм и 48-битную целочисленную арифметику.

И довольно хороший псевдослучайный источник - это функция arc4random(), доступная во многих системах. Не являясь частью какого-либо официального стандарта, появился в BSD около 1997 года, но вы можете найти его на таких системах, как Linux и macOS/iOS.

Ответ 24

Для приложений 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;
    }
}

Ответ 25

Мое минималистичное решение должно работать для случайных чисел в диапазоне [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));
} 

Ответ 26

Попробуйте это, я изложил это из некоторых понятий, уже упомянутых выше:

/*    
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;
}