Функции rand() и srand()

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

В языке C для получения случайного числа используется функция rand(), являющаяся частью стандартной библиотеки. Эта функция не требует аргументов.

Задание:

Напишите программу, в которой результат выполнения функции rand() присваивается целочисленной переменной. Отобразите значение этой переменной на экране.

Функция rand() возвращает целое число в диапазоне от 0 до RAND_MAX, где RAND_MAX зависит от системы и определяется в stdlib.h. Например, это может быть 32767 или 2147483647, в зависимости от размера целого числа в системе.

Задание:

Узнайте значение RAND_MAX на вашей системе. Подключите заголовочный файл stdlib.h в файл вашего исходного кода.

Пример кода для вывода 50 случайных чисел на экран:

#include <stdio.h>
#include <stdlib.h>
 
main () {
     char i;
 
     for (i = 1; i <= 50; i++) {
          printf("%15d", rand());
          if (i % 5 == 0) printf("\n");
     }
 
}

В цикле после каждых пяти чисел происходит переход на новую строку. Это достигается благодаря проверке остатка от деления i на 5. Значение переменной i изначально равно единице, чтобы переход строки не происходил после первого числа.

Задание:

Перепишите приведенный код. Запустите программу несколько раз и проанализируйте, меняются ли получаемые результаты при каждом запуске.

Однако, вы могли заметить, что числа при каждом запуске остаются неизменными, даже после рекомпиляции. Это происходит, потому что начальное значение для формулы генерации чисел всегда одинаково. Тем не менее, это начальное значение можно изменить с помощью функции srand(), которой передается любое целое число. Если задать аргумент, например, srand(1000), то результаты программы также станут постоянными, хотя и другими. Возникает вопрос, как сделать аргумент для srand() случайным?

Задание:

Измените программу так, чтобы пользователь мог ввести любое целое число через scanf(), и это число передавалось бы в srand().

Пользователь может задать начальное значение, однако это не всегда удобно. Поэтому часто привязывают начальное значение к системному процессу, например, к времени, которое всегда уникально. Преобразовав системное время в целое число, можно передать его в srand().

Функция time(), определенная в заголовочном файле time.h, возвращает текущее время как целое число. Используя srand(time(NULL));, можно задать различные начальные значения для rand().

Задание:

Переделайте программу так, чтобы начальное значение зависело от текущего системного времени.

Получение целых случайных чисел в заданных диапазонах

Функция rand() предоставляет случайное число в пределах от 0 до RAND_MAX. Но как быть, если необходимо получать случайные числа в других диапазонах, например, от 100 до 999?

Рассмотрим задачу получения чисел от 0 до 5. Делая любую int переменную целочисленным делением на 5, получаем результат в диапазоне от 0 до 4. Например, если rand() возвращает 283, остаток от деления на 5 будет 3, что обеспечит диапазон [0, 5).

Если требуется включить 5 в диапазон [0, 5], надо делить на 6. Важно: размер диапазона определяет количество возможных значений. Для включения максимума, например, если необходимо, чтобы максимум не приходил в диапазон, прибавление единицы не требуется.

Задание:

Напишите программу для вывода 50 случайных чисел от 0 до 99.

Формула для получения случайного числа в диапазоне [a, b]:

rand() % длина_диапазона + сдвиг

где длина_диапазона это b - a + 1, а сдвиг равен a.

Задание:

Выведите случайные числа в диапазоне от 100 до 299.

Также возможна генерация случайных отрицательных чисел. Диапазон [-35, -1] имеет длину 35, что соответствует действительности; и выражение становиться rand() % 35 - 35.

Задание:

Создайте программу, выводящую случайные числа от -128 до 127.

Получение вещественных случайных чисел

Для вещественных чисел ситуация несколько иная. Остаток при делении не применяется для дробных чисел, равно как при вычислении длины диапазона не прибавляется единица.

Рассмотрим, например, диапазон [2.50, 5.30]. В этом случае количество значений не фиксировано, так как дробные числа имеют различную точность представления. Оставляем только вычитание минимума из максимума для длины диапазона.

При разделении случайного числа rand(), приведенного к типу float, на RAND_MAX, получается число в границах от 0 до 1. Умножая это на длину диапазона и добавляя минимальный сдвиг, число оказывается в нужных границах. Формула приобретает вид:

(float) rand() / RAND_MAX * (max - min) + min

Задание:

Заполните массив дробными числами из диапазона от 0.51 до 1.00 и выведите элементы массива на экран.

Равновероятные случайные числа

Функция rand() генерирует любое число от 0 до RAND_MAX с равными шансами. Каждое из значений имеет одинаковую вероятность появления. Например, у числа 100 такой же шанс, как и у 25876.

Написав программу для подсчета выпадений значений, можно доказать это. Для большой выборки диапазон значений будет мал, и различия в вероятностях незначительны.

#include <stdio.h>
#include <time.h>
 
#define N 500
 
main () {
  int i;
  int arr[5] = {0};
 
  srand(time(NULL));
 
  for (i=0; i < N; i++)
    switch (rand() % 5) {
      case 0: arr[0]++; break;
      case 1: arr[1]++; break;
      case 2: arr[2]++; break;
      case 3: arr[3]++; break;
      case 4: arr[4]++; break;
    }
 
  for (i=0; i < 5; i++)
    printf("%d - %.2f%%\n", i, ((float) arr[i] / N) * 100);
 
}

В программе массив из пяти элементов изначально заполняется нулями. Случайные числа от 0 до 4 увеличивают значение соответствующего элемента. В конце отображаются проценты выпадения каждого значения.

Задание:

Воспроизведите программу и проверьте результаты с N, равным 10, 50, 500, 5000, 50000. Объясните различия.

Вопросы для самопроверки:

  1. Что делает функция rand() в языке C?
  2. Какой тип возвращает функция rand() и в каком диапазоне?
  3. Для чего используется функция srand()?
  4. Как можно генерировать случайные отрицательные числа, например, в диапазоне от -35 до -1?
  5. Как получить случайное вещественное число в диапазоне от 0.51 до 1.00?
  6. Почему результаты программы с генерацией случайных чисел становятся одинаковыми, если не использовать srand()?

Программа курса:

  1. Описание курса
  2. Введение в язык программирования C
  3. Типы данных в C и форматированный вывод
  4. Символьные типы и управляющие символы в C
  5. Операторы ветвления и switch в C
  6. Циклы и операторы в языке C
  7. Битовые операции в языке C
  8. Посимвольный ввод и вывод в C - буферизация
  9. Переменные, адреса и указатели в C
  10. Передача аргументов по ссылке и значению в C
  11. Форматированный ввод данных с использованием scanf
  12. Генерация псевдослучайных чисел на C
  13. Адресная арифметика в массивах C
  14. Передача массивов в функции и указатели
  15. Строки в языке C - особенности и функции работы
  16. Функции работы со строками в C
  17. Работа со структурами в C - создание и применение
  18. Динамические структуры данных в C
  19. Ввод и вывод данных из файлов в языке C
  20. Передача аргументов в C и работа с файлами
  21. Препроцессор в языке C - директивы и макросы
  22. Создание и компиляция многофайловых программ в C
  23. Использование статических и динамических библиотек в C