Сформировать случайное число в диапазоне c. Псевдослучайные числа. Ограничить случайные числа сверху

Функция, генерирующая псевдослучайные числа, имеет прототип в файле библиотеки stdlib.h :

1
2
3
4
5
6

unsigned long int next = 1;
int rand(void )
{
next = next * 1103515245;
return ((unsigned int )(next / 65536) * 2768);
}


Функция rand() не принимает аргументов, а оперирует переменной next с глобальной областью видимости.

Если необходимо сгенерировать последовательность в диапазоне , то используется формула:

Number = rand()%(M2-M1+1) + M1;

где Number – генерируемое число. M2-M1+1 – полный диапазон представления чисел. M1 – смещение указанного диапазона относительно 0; % — остаток от деления .

Например, если требуется сгенерировать последовательность в диапазоне [-10;10], то вызов функции будет выглядеть как

Number = rand()%(10+10+1)-10

Number = rand()%(21)-10

В результате получения остатка от деления на 21 имеем число от 0 до 20. Вычитая из полученного числа 10, получим число в искомом диапазоне [-10;10].

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

Для генерации различных последовательности при каждом запуске программы необходимо проинициализировать глобальную переменную next значением, отличным от 1. С этой целью используется функция
void srand(unsigned int seed)
{ next = seed; }

Чтобы инициализация next при каждом запуске программы была различной в качестве аргумента seed чаще всего используется текущее время.

Пример Заполнить массив из 20 элементов случайными числами в диапазоне от 0 до 99.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include
#include
#include
#define SIZE 20
int main() {
int a;
srand(time(NULL ));
for (int i = 0; i {
a[i] = rand() % 100;
printf("%d " , a[i]);
}
getchar();
return 0;
}


Результат выполнения

Часто возникает задача расставить уже имеющийся набор значений в произвольном порядке. С этой целью также используется генератор псевдослучайных чисел. При этом создается массив и заполняется значениями.
Сама процедура перемешивания происходит следующим образом. Генерируется два значения индексов массива случайным образом, и значения элементов с полученными индексами меняются местами. Процедура повторяется не менее N раз, где N - количество элементов массива.
В качестве примера рассмотрим перемешивание 20 значений (от 1 до 20) и повторим процедуру 20 раз.

Реализация на Си

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#include
#include
#include
#define SIZE 20
int main() {
int a;
srand(time(NULL ));

for (int i = 0; i < SIZE; i++)
{
a[i] = i + 1;
printf("%2d " , a[i]);
}
for (int i = 0; i < SIZE; i++)
{
// Генерируем случайно два индекса элементов
int ind1 = rand() % 20;
int ind2 = rand() % 20;
// и меняем местами элементы с этими индексами
int temp = a;
a = a;
a = temp;
}
printf("\n" );

for (int i = 0; i < SIZE; i++)
printf("%2d " , a[i]);
getchar();
return 0;
}


Результат выполнения


Часто возникает задача произвольного выбора ранее заданных элементов массива. Причем необходимо предусмотреть отсутствие повторений в выборе этих элементов.
Алгоритм такого выбора состоит в следующем:

  • Выбираем произвольно индекс элемента массива
  • Если элемент с таким индексом уже был ранее выбран, двигаемся вправо, пока не дойдём до следующего не выбранного элемента. При этом следим за тем, чтобы "движение вправо" не вышло за границы массива. Если фиксируется выход за границы массива, начинаем просмотр элементов массива с начала.
  • Выбираем элемент
  • Фиксируем элемент как выбранный
  • Повторяем указанные действия для всех остальных элементов

Реализации на Си
В результате получаем новый массив b , сформированный произвольной выборкой элементов массива a .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#include
#include
#include
#define SIZE 20
int main() {
int a;
int b; // результирующий массив
srand(time(NULL ));
// Заполняем массив последовательными значениями от 1 до 20
for (int i = 0; i < SIZE; i++)
{
a[i] = i + 1;
printf("%2d " , a[i]);
}

for (int i = 0; i < SIZE; i++)
{
int ind = rand() % 20; // выбираем произвольный индекс
while (a == -1) // пока элемент "выбран"
{
ind++; // двигаемся вправо
ind %= 20; // если дошли до правой границы, возвращаемся в начало
}
b[i] = a; // записываем следующий элемент массива b
a = -1; // отмечаем элемент массива a как "выбранный"
}
printf("\n" );
// Выводим получившийся массив
for (int i = 0; i < SIZE; i++)
printf("%2d " , b[i]);
getchar();
return 0;
}


Результат выполнения

Теги: си рандом, си случайные числа, генерация случайных чисел, ГСЧ, псевдослучайные числа, метод монте-карло

Псевдослучайные числа

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

С примерами использования случайных чисел вы сталкиваетесь повсюду. Псевдослучайные числа используются в дизайне и графике, для генерации уровней в компьютерных играх и симулирования ИИ. Наборы случайных чисел используются в математических алгоритмах (см. Методы Монте-Карло).

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

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

Посмотрим стандартный генератор.

#include #include #include int main() { int i, r; srand(42); for (i = 0; i < 10; i++) { r = rand(); printf("%d\n", r); } _getch(); return 0; }

Для начала необходимо инициализировать генератор случайных чисел (ГСЧ, или RNG - random number generator), задать зерно – seed, на основе которого в дальнейшем будет происходить генерация. Важно, что для одного и того же начального значения генератор будет возвращать одни и те же числа.

Srand(42);

Присваиваем переменной r случайное значение

R = rand();

Значение будет лежать в диапазоне от 0 до RAND_MAX.

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

Srand(time(NULL));

Srand(_getpid());

Функция getpid библиотеки process.h возвращает идентификатор процесса (можно также использовать getpid, не POSIX версию функции).

Центральная Предельная Теорема

Очень важно сразу напомнить или познакомить с центральной предельной теоремой. Неформальное определение – распределение суммы слабо зависимых случайных величин стремится к нормальному. Пальцеобразное объяснение: если сложить несколько случайных величин, независимо от их распределения, то распределение суммы будет нормальным. Часто можно увидеть такой код

#include #include #include int main() { int i, r, r1, r2, r3; srand(time(NULL)); r1 = rand(); r2 = rand(); r3 = rand(); r = (r1 + r2 + r3) / 3; printf("%d", r); _getch(); return 0; }

Генерация случайных чисел на заданном отрезке

Во-первых, получим случайное число от нуля до единицы:

Const float RAND_MAX_F = RAND_MAX; float get_rand() { return rand() / RAND_MAX_F; }

Для получения числа в отрезке от нуля до N умножим N на случайное число от нуля до единицы. Для получения случайного числа от M До N, сдвинем полученное число на M.

Float get_rand_range(const float min, const float max) { return get_rand() * (max - min) + min; }

Для получения целого числа, будем брать остаток от деления на длину интервала. Но остаток от деления будет возвращать число на единицу меньше, чем наш интервал, поэтому увеличим его на единицу:

Int get_rand_range_int(const int min, const int max) { return rand() % (max - min + 1) + min; }

Пример использования случайных чисел для вычисления интеграла. Пусть у нас есть некоторая гладкая функция от одной переменной. Ограничим её квадратом от a до b, и от 0 до некоторой точки, которая заведомо больше нашей функции.

Будем случайным образом кидать точки на нашем квадрате. Если они лежат выше функции (на рисунке изображены зелёными крестиками), то отнесём их к первой группе A, если ниже функции (на рисунке красные), то отнесём их ко второй группе B. Положение точек случайное и распределено равномерно (т.к. стандартный генератор даёт равномерное распределение. Этот простой пример, кстати, уже показывает, насколько важно знать свойства ГСЧ). Тогда отношение красных точек к общему числу точек будет равно отношению площади под графиком к общей площади. А общая площадь – это квадрат (b-a) на q.

Src="/images/c_random_integral.png" alt="Всё, что случайно попадает выше нашей функции - зелёное, всё что ниже - красное.
Отношение зелёного к красному будет равно отношению площади над графиком к площади под графиком."> Всё, что случайно попадает выше нашей функции - зелёное, всё что ниже - красное.
Отношение зелёного к красному будет равно отношению площади над графиком к площади под графиком.

Применим наши выкладки – найдём интеграл функции x^2 на отрезке от 0 до двух двумя способами.

#include #include #include #include #include const float RAND_MAX_F = RAND_MAX; float get_rand() { return rand() / RAND_MAX_F; } float get_rand_range(const float min, const float max) { return get_rand() * (max - min) + min; } #define ROUNDS 1000 float fun(float x) { return x * x; } float square_square(float a, float b, float q) { float h = (b - a) / (float)ROUNDS; float sum = 0; for (; a < b; a += h) { sum += fun(a) * h; } return sum; } float rand_square(float a, float b, float q) { float res; float x, y; int i; int lower = 0; float ratio; float square; srand(time(NULL)); for (i = 0; i < ROUNDS; i++) { x = get_rand_range(a, b); y = get_rand_range(0, q); res = fun(x); if (res > y) { lower++; } } ratio = (float)lower / (float)ROUNDS; square = (b - a) * q * ratio; return square; } int main() { float abs_ans = 2.66667f; float sr = rand_square(0, 2, 4); float ss = square_square(0, 2, 4); printf("Rounds = %d\n", ROUNDS); printf("Sa = %.5f\n", abs_ans); printf("Sr = %.5f\n", sr); printf("Ss = %.5f\n", ss); printf("dr = %.5f\n", fabs(sr - abs_ans)); printf("ds = %.5f\n", fabs(ss - abs_ans)); _getch(); return 0; }

Поиграйте со значением ROUNDS, измените его и посмотрите, как меняется точность вычислений.

Генерация истинно случайных чисел

Для генерации настоящих случайных чисел используют генераторы, основанные на каких-то случайных физических процессах. Например, на тепловых шумах, на подсчёте числа делений радиоактивного вещества, на атмосферных шумах и т.п. Недостаток таких генераторов – низкая скорость работы (количество сгенерированных чисел в секунду) ; конечно, такие генераторы обычно являются отдельным устройством.

У нас есть последовательность чисел, состоящая из практически независимых элементов, которые подчиняются заданному распределению. Как правило, равномерному распределению.

Сгенерировать случайные числа в Excel можно разными путями и способами. Рассмотрим только лучше из них.

Функция случайного числа в Excel

  1. Функция СЛЧИС возвращает случайное равномерно распределенное вещественное число. Оно будет меньше 1, больше или равно 0.
  2. Функция СЛУЧМЕЖДУ возвращает случайное целое число.

Рассмотрим их использование на примерах.

Выборка случайных чисел с помощью СЛЧИС

Данная функция аргументов не требует (СЛЧИС()).

Чтобы сгенерировать случайное вещественное число в диапазоне от 1 до 5, например, применяем следующую формулу: =СЛЧИС()*(5-1)+1.

Возвращаемое случайное число распределено равномерно на интервале .

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

  1. Щелкаем по ячейке со случайным числом.
  2. В строке формул выделяем формулу.
  3. Нажимаем F9. И ВВОД.

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


Диапазон вертикальных значений – частота. Горизонтальных – «карманы».



Функция СЛУЧМЕЖДУ

Синтаксис функции СЛУЧМЕЖДУ – (нижняя граница; верхняя граница). Первый аргумент должен быть меньше второго. В противном случае функция выдаст ошибку. Предполагается, что границы – целые числа. Дробную часть формула отбрасывает.

Пример использования функции:

Случайные числа с точностью 0,1 и 0,01:

Как сделать генератор случайных чисел в Excel

Сделаем генератор случайных чисел с генерацией значения из определенного диапазона. Используем формулу вида: =ИНДЕКС(A1:A10;ЦЕЛОЕ(СЛЧИС()*10)+1).

Сделаем генератор случайных чисел в диапазоне от 0 до 100 с шагом 10.

Из списка текстовых значений нужно выбрать 2 случайных. С помощью функции СЛЧИС сопоставим текстовые значения в диапазоне А1:А7 со случайными числами.

Воспользуемся функцией ИНДЕКС для выбора двух случайных текстовых значений из исходного списка.

Чтобы выбрать одно случайное значение из списка, применим такую формулу: =ИНДЕКС(A1:A7;СЛУЧМЕЖДУ(1;СЧЁТЗ(A1:A7))).

Генератор случайных чисел нормального распределения

Функции СЛЧИС и СЛУЧМЕЖДУ выдают случайные числа с единым распределением. Любое значение с одинаковой долей вероятности может попасть в нижнюю границу запрашиваемого диапазона и в верхнюю. Получается огромный разброс от целевого значения.

Нормальное распределение подразумевает близкое положение большей части сгенерированных чисел к целевому. Подкорректируем формулу СЛУЧМЕЖДУ и создадим массив данных с нормальным распределением.

Себестоимость товара Х – 100 рублей. Вся произведенная партия подчиняется нормальному распределению. Случайная переменная тоже подчиняется нормальному распределению вероятностей.

При таких условиях среднее значение диапазона – 100 рублей. Сгенерируем массив и построим график с нормальным распределением при стандартном отклонении 1,5 рубля.

Используем функцию: =НОРМОБР(СЛЧИС();100;1,5).

Программа Excel посчитала, какие значения находятся в диапазоне вероятностей. Так как вероятность производства товара с себестоимостью 100 рублей максимальная, формула показывает значения близкие к 100 чаще, чем остальные.

Перейдем к построению графика. Сначала нужно составить таблицу с категориями. Для этого разобьем массив на периоды:

На основе полученных данных сможем сформировать диаграмму с нормальным распределением. Ось значений – число переменных в промежутке, ось категорий – периоды.

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

На помощь нам приходит функция стандартной библиотеки языка Си (не C++) rand() .

int rand (void);

Она генерирует псевдослучайное целое число на интервале значений от 0 до RAND_MAX . Последнее является константой, которая варьируется в зависимости от реализации языка, но в большинстве случаев составляет 32767.
А что если нам нужны случайные числа от 0 до 9? Типичный выход из ситуации — использование операции деления по модулю.

Если нам нужны числа от 1 (а не от 0) до 9, то можно прибавить единичку…

Идея такая: генерируем случайное число от 0 до 8, и после прибавления 1 оно превращается в случайное число от 1 до 9.

И последнее, самое печальное.
К сожалению, функция rand() генерирует псевдослучайные числа, т.е. числа, которые кажутся случайными, но на самом деле являются последовательностью значений, вычисленных по хитрому алгоритму, в качестве параметра принимающему так называемое зерно (seed). Т.е. сгенерированные функцией rand() числа будут зависеть от значения, которое имеет зерно в момент ее вызова. А зерно всегда устанавливается компилятором в значение 1. Иными словами, последовательность чисел будет хоть и псевдослучайной, но всегда одинаковой.
А это не то, что нам надо.

Исправить ситуацию помогает функция srand() .

void srand (unsigned int seed);

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

Но проблема осталась. Как сделать случайным зерно, ведь от него все зависит?
Типичный выходит из ситуации — использование функции time() .

time_t time (time_t* timer);

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

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

Для использования функций rand() и srand() нужно подключить заголовочный файл , а для использования time() — файл .

Вот полноценный пример.

#include
#include
#include

using namespace std;

int main()
{
cout << "10 random numbers (1..100): " << endl;
srand(time(NULL));
for(int i=0;i<10;i++) cout << rand() % 100 + 1 << " ";
cin.get();
return 0;
}

Приветствую всех, кто заскочил. В это короткой заметке будет пара слов о генерации псевдослучайных чисел на C/C++. В частности о том, как работать с самой простой функцией генерации — rand().

Функция rand()

Находится в стандартной библиотеке С++(stdlib.h). Генерирует и возвращает псевдослучайное число в диапазоне от 0 до RAND_MAX . Эта константа может отличаться в зависимости от компилятора или архитектуры процессора, в основном, это максимальное значение типа данных unsigned int . Параметров не принимает и никогда не принимала.

Для того, чтобы генерация свершилась, нужно выставить семя(seed) с помощью вспомогательной функции из той же библиотеки — srand() . Она принимает число и ставит его в качестве отправной точки в генерации случайного числа. Если семя не выставить, то при каждом запуске программы, мы будет получать одинаковые случайные числа. Самым очевидным решением является отправить туда текущее системное время. Сделать это можно с помощью функции time(NULL). Эта функция находится в библиотеке time.h .

С теорией разобрались, нашли все функции для генерации случайных чисел, теперь давайте погенерируем.

Пример использования функции генерации случайных чисел rand()

#include #include #include using namespace std; int main() { srand(time(NULL)); for(int i = 0; i < 10; i++) { cout << rand() << endl; } return 0; }

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

Выставить границы диапазона для rand()

Чтобы сгенерировать число в диапазоне от A до B включительно, нужно написать так:

A + rand() % ((B + 1) - A);

Значения границ могут быть в том числе и отрицательными, что позволяет генерировать отрицательное случайное число, посмотрим пример.

#include #include #include using namespace std; int main() { srand(time(NULL)); int A = -2; int B = 8; for(int i = 0; i < 100; i++) { cout << A + rand() % ((B + 1) - A) << endl; } return 0; }