return v;
}
И вновь простая операция над объектом класса
Matrix
делает за нас большую часть работы. Как указывалось в разделе 24.5.3, операции вывода объектов класса
Matrix
описаны в заголовке
MatrixIO.h
. Функции
random_matrix()
и
random_vector()
просто используют случайные числа (раздел 24.7). Читатели могут написать эти функции в качестве упражнения. Имя
Index
является синонимом типа индекса, используемого в библиотеке
Matrix
, и определено с помощью оператора
typedef
(раздел A.15). Этот тип включается в программу с помощью объявления
using
.
using Numeric_lib::Index;
24.7. Случайные числа
Если вы попросите любого человека назвать случайное число, то они назовут 7 или 17, потому что эти числа считаются самыми случайными. Люди практически никогда не называют число нуль, так как оно кажется таким идеально круглым числом, что не воспринимается как случайное, и поэтому его считают наименее случайным числом. С математической точки зрения это полная бессмыслица: ни одно отдельно взятое число нельзя назвать случайным. То, что мы часто называем случайными числами — это последовательность чисел, которые подчиняются определенному закону распределения и которые невозможно предсказать, зная предыдущие числа. Такие числа очень полезны при тестировании программ (они позволяют генерировать множество тестов), в играх (это один из способов гарантировать, что следующий шаг в игре не совпадет с предыдущим) и в моделировании (мы можем моделировать сущность, которая ведет себя случайно в пределах изменения своих параметров).

Как практический инструмент и математическая проблема случайные числа в настоящее время достигли настолько высокой степени сложности, что стали широко использоваться в реальных приложениях. Здесь мы лишь коснемся основ теории случайных чисел, необходимых для осуществления простого тестирования и моделирования. В заголовке
<cstdlib>
из стандартной библиотеки есть такой код:
int rand(); // возвращает числа из диапазона
// [0:RAND_MAX]
RAND_MAX // наибольшее число, которое генерирует
// датчик rand()
void srand(unsigned int); // начальное значение датчика
// случайных чисел
Повторяющиеся вызовы функции
rand()
генерируют последовательность чисел типа
int
, равномерно распределенных в диапазоне
[0:RAND_MAX]
. Эта последовательность чисел называется псевдослучайной, потому что она генерируется с помощью математической формулы и с определенного места начинает повторяться (т.е. становится предсказуемой и не случайной). В частности, если мы много раз вызовем функцию
rand()
в программе, то при каждом запуске программы получим одинаковые последовательности. Это чрезвычайно полезно для отладки. Если же мы хотим получать разные последовательности, то должны вызывать функцию
srand()
с разными значениями. При каждом новом аргументе функции
srand()
функция
rand()
будет порождать разные последовательности.
Например, рассмотрим функцию
random_vector()
, упомянутую в разделе 24.6.3. Вызов функции
random_vector(n)
порождает объект класса
Matrix<double,1>
, содержащий
n
элементов, представляющих собой случайные числа в диапазоне от
[0:n]
:
Vector random_vector(Index n)
{
Vector v(n);
for (Index i = 0; i < n; ++i)
v(i) = 1.0 * n * rand() / RAND_MAX;
return v;
}
Обратите внимание на использование числа
1.0
, гарантирующего, что все вычисления будут выполнены в арифметике с плавающей точкой. Иначе при каждом делении целого числа на
RAND_MAX
мы получали бы
0
.
Сложнее получить целое число из заданного диапазона, например
[0:max]
. Большинство людей сразу предлагают следующее решение:
int val = rand()%max;
Долгое время такой код считался совершенно неудовлетворительным, поскольку он просто отбрасывает младшие разряды случайного числа, а они, как правило, не обладают свойствами, которыми должны обладать числа, генерируемые традиционными датчиками случайных чисел. Однако в настоящее время во многих операционных системах эта проблема решена достаточно успешно, но для обеспечения переносимости своих программ мы рекомендуем все же скрывать вычисления случайных чисел в функциях.
int randint(int max) { return rand()%max; }
int randint(int min, int max) { return randint(max–min)+min; }
Таким образом, мы можем скрыть определение функции
randint()
, если окажется, что реализация функции
rand()
является неудовлетворительной. В промышленных программных системах, а также в приложениях, где требуются неравномерные распределения, обычно используются качественные и широко доступные библиотеки случайных чисел, например
Boost::random
. Для того чтобы получить представление о качестве вашего датчика случайных чисел, выполните упр. 10.
24.8. Стандартные математические функции
В стандартной библиотеке есть стандартные математические функции (
cos
,
sin
,
log
и т.д.). Их объявления можно найти в заголовке
<cmath>
.
Стандартные математические функции могут иметь аргументы типов
float
,
double
,
long double
и
complex
(раздел 24.9). Эти функции очень полезны при вычислениях с плавающей точкой. Более подробная информация содержится в широко доступной документации, а для начала можно обратиться к документации, размещенной в веб.