Генерирование случайных чисел в C

При поиске учебников по генерации случайных чисел в C я нашел этот раздел

Когда я пытаюсь использовать функцию rand() без параметров, я всегда получаю 0. Когда я пытаюсь использовать функцию rand() с параметрами, я всегда получаю значение 41. И всякий раз, когда я пытаюсь использовать arc4random() и random(), я получаю ошибку LNK2019.

Вот что я сделал:

#include <stdlib.h>
int main()
{
  int x;
  x = rand(6);
  printf("%d", x);
}

Этот код всегда генерирует 41. Где я ошибаюсь? Я запускаю Windows XP SP3 и использую VS2010 Командная строка как компилятор.

Ответ 1

Вы должны вызвать srand() перед вызовом rand для инициализации генератора случайных чисел.

Либо вызовите его с определенным семенем, и вы всегда будете получать одну и ту же псевдослучайную последовательность

#include <stdlib.h>

int main ()
{
  srand ( 123 );
  int random_number = rand();
  return 0;
}

или вызвать его с изменяющимися источниками, то есть функцией времени

#include <stdlib.h>
#include <time.h>

int main ()
{
  srand ( time(NULL) );
  int random_number = rand();
  return 0;
}

В ответ на комментарий Луны rand() генерирует случайное число с равной вероятностью между 0 и RAND_MAX (макросом, предварительно определенным в stdlib.h)

Затем вы можете сопоставить это значение с меньшим диапазоном, например

int random_value = rand(); //between 0 and RAND_MAX

//you can mod the result
int N = 33;
int rand_capped = random_value % N;  //between 0 and 32
int S = 50;
int rand_range = rand_capped + S; //between 50 and 82

//you can convert it to a float
float unit_random = random_value / (float) RAND_MAX; //between 0 and 1 (floating point)

Этого может быть достаточно для большинства применений, но его ценность указывает на то, что в первом случае использование оператора mod вводит небольшое смещение, если N равномерно не делит на RAND_MAX + 1.

Генераторы случайных чисел интересны и сложны, широко говорят, что генератор rand() в стандартной библиотеке C не является генератором случайных чисел большого качества, читайте (http://en.wikipedia.org/wiki/Random_number_generation для определения качества).

http://en.wikipedia.org/wiki/Mersenne_twister (источник http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html) является популярным генератором случайных чисел высокого качества.

Кроме того, я не знаю arc4rand() или random(), поэтому я не могу комментировать.

Ответ 2

Вам нужно засеять PRNG, чтобы он начинался с другого значения каждый раз.

Простым, но низкокачественным семенем является использование текущего времени:

srand(time(0));

Это поможет вам начать, но считается низким качеством (например, не используйте это, если вы пытаетесь создать ключи RSA).

Фон. Генераторы псевдослучайных чисел не создают истинные последовательности случайных чисел, а просто имитируют их. Учитывая номер начальной точки, PRNG всегда будет возвращать одну и ту же последовательность чисел. По умолчанию они начинаются с одного и того же внутреннего состояния, поэтому возвращают ту же последовательность.

Чтобы не получить одну и ту же последовательность, вы меняете внутреннее состояние. Акт изменения внутреннего состояния называется "посев".

Ответ 3

#include <stdlib.h>

int main()
{
    int x;
    x = rand(6);
    printf("%d", x);
}

Особенно, как новичок, вы должны попросить своего компилятора распечатать каждое предупреждение о некорректном коде, который он может сгенерировать. Современные компиляторы знают множество различных предупреждений, которые помогут вам лучше программировать. Например, когда вы компилируете эту программу с помощью компилятора GNU C:

$ gcc -W -Wall rand.c
rand.c: In function `main':
rand.c:5: error: too many arguments to function `rand'
rand.c:6: warning: implicit declaration of function `printf'

Здесь вы получаете два предупреждения. Первый говорит, что функция rand принимает только нулевые аргументы, а не одну, как вы пробовали. Чтобы получить случайное число от 0 до n, вы можете использовать выражение rand() % n, которое не идеально, но нормально для малых n. Полученные случайные числа обычно распределяются неравномерно; меньшие значения возвращаются чаще.

Второе предупреждение сообщает вам, что вы вызываете функцию, которую компилятор не знает в этот момент. Вы должны сообщить компилятору, сказав #include <stdio.h>. Для чего нужны файлы, для которых функции не всегда просты, но во многих случаях часто задавать спецификацию Open Group для переносных операционных систем: http://www.google.com/search?q=opengroup+rand.

Эти два предупреждения рассказывают вам об истории языка программирования C. 40 лет назад определение функции не включало количество параметров или типы параметров. Также было нормально называть неизвестную функцию, которая в большинстве случаев работала. Если вы хотите писать код сегодня, вы не должны полагаться на эти старые функции, но вместо этого включайте предупреждения своего компилятора, понимаете предупреждения и затем исправляете их правильно.

Ответ 4

Кроме того, линейные конгруэнтные PRNG имеют тенденцию создавать больше случайности на более высоких битах, чем на младших битах, поэтому для ограничения результата не используйте modulo, а вместо этого используйте что-то вроде:

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

(Это один из "Численных рецептов в C", ch.7)

Ответ 5

Сначала вам нужно засеять генератор, потому что он не генерирует реальные случайные числа!

Попробуйте следующее:

#include <stdlib.h>
#include <time.h>
int main()
{
    // random seed, time!
    srand( time(NULL) ); // hackish but gets the job done.
    int x;
    x = rand(); // everytime it is different because the seed is different.
    printf("%d", x);
}

Ответ 6

Или, чтобы получить псевдослучайный int в диапазоне от 0 до 19, например, вы могли бы использовать более высокие биты, например:

j = ((rand() >> 15) % 20;

Ответ 7

int *generate_randomnumbers(int start, int end){
    int *res = malloc(sizeof(int)*(end-start));
    srand(time(NULL));
    for (int i= 0; i < (end -start)+1; i++){
        int r = rand()%end + start;
        int dup = 0;
        for (int j = 0; j < (end -start)+1; j++){
            if (res[j] == r){
                i--;
                dup = 1;
                break;
            }
        }
        if (!dup)
            res[i] = r;
    }
    return res;
}