Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 254 255 256 257 258 259 260 261 262 ... 337
= dot_product(m[i], u);

  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). Эти функции очень полезны при вычислениях с плавающей точкой. Более подробная информация содержится в широко доступной документации, а для начала можно обратиться к документации, размещенной в веб.

 

 Если стандартная математическая функция не может дать корректного результата, она устанавливает флажок errno. Рассмотрим пример.

errno = 0;

double s2 = sqrt(–1);

if (errno) cerr << "Что-то где-то пошло не так, как надо";

if (errno == EDOM) // ошибка из-за выхода аргумента

                   // за пределы области определения

  cerr << " фунция sqrt() для отрицательных аргументов не определена.";

pow(very_large,2); // плохая идея

if (errno==ERANGE) // ошибка из-за выхода за пределы допустимого

                   // диапазона

  cerr << "pow(" << very_large

       << ",2) слишком большое число для double";

Если вы выполняете серьезные математические вычисления, то всегда должны проверять значение errno, чтобы убедиться, что после возвращения результата оно по-прежнему равно 0. Если нет, то что-то пошло не так, как надо. Для того чтобы узнать, какие математические функции могут устанавливать флажок errno и чему он может быть равен, обратитесь к документации.

 

 Как показано в примере, ненулевое значение флажка errno просто означает, что что-то пошло не так. Функции, не входящие в стандартную библиотеку, довольно часто также устанавливают флажок errno при выявлении ошибок, поэтому следует точнее анализировать разные значения переменной errno, чтобы понять, что именно случилось. В данном примере до вызова стандартной библиотечной
1 ... 254 255 256 257 258 259 260 261 262 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий