новая версия языка C++ (C++0x)), поэтому пока приходится прибегать к трюкам с инициализацией массива (в данном случае init_pos) и использовать его для инициализации объектов класса Matrix. Мы можем использовать объекты start_row и clear_row следующим образом:
board[white_start_row] = start_row; // расставить белые фигуры
for (int i = 1; i<7; ++i) board[i] = clear_row; // очистить середину
// доски
board[black_start_row] = start_row; // расставить черные фигуры
Обратите внимание на то, что когда мы извлекли строку, используя выражение [i], мы получили значение lvalue (см. раздел 4.3); иначе говоря, мы можем присвоить результат элементу board[i].
24.5.4. Ввод-вывод объектов класса Matrix
Библиотека Matrix предоставляет очень простые средства для ввода и вывода одно- и двухмерных объектов класса Matrix:
Matrix<double> a(4);
cin >> a;
cout << a;
Этот фрагмент кода прочитает четыре разделенные пробелами числа типа double, заключенных в фигурные скобки; например:
{ 1.2 3.4 5.6 7.8 }
Вывод очень прост, поэтому мы просто можем увидеть то, что ввели. Механизм ввода-вывода двумерных объектов класса Matrix просто считывает и записывает последовательности одномерных объектов класса Matrix, заключенные в квадратные скобки. Рассмотрим пример.
Matrix<int,2> m(2,2);
cin >> m;
cout << m;
Он прочитает запись
{
{ 1 2 }
{ 3 4 }
}
Вывод очень похож.
Операторы << и >> из класса Matrix позволяют писать простые программы. В более сложных ситуациях нам потребуется заменить их своими операторами. По этой причине определение операторов << и >> из класса Matrix помещены в заголовок MatrixIO.h (а не Matrix.h), поэтому, для того чтобы использовать матрицы в своей программе, вам не обязательно включать заголовок MatrixIO.h.
24.5.5. Трехмерный объект класса Matrix
По существу, трехмерные объекты класса Matrix, как и матрицы более высоких размерностей, похожи на двумерные, за исключением того, что они имеют больше размерностей. Рассмотрим пример.
Matrix<int,3> a(10,20,30);
a.size(); // количество элементов
a.dim1(); // количество элементов в размерности 1
a.dim2(); // количество элементов в размерности 2
a.dim3(); // количество элементов в размерности 3
int* p = a.data(); // извлекает данные по указателю (в стиле языка С)
a(i,j,k); // (i,j,k)-й элемент (в стиле языка Fortran)
// с проверкой диапазона
a[i]; // i-я строка (в стиле языка C)
// с проверкой диапазона
a[i][j][k]; // (i,j,k)-й элемент (в стиле языка С)
a.slice(i); // строки от i-й до последней
a.slice(i,j); // строки от i-й до j-й
Matrix<int,3> a2 = a; // копирующая инициализация
a = a2; // копирующее присваивание
a *= 7; // пересчет (и +=, –=, /= и т.д.)
a.apply(f); // a(i,j,k)=f(a(i,j,k)) для каждого элемента a(i,j,k)
a.apply(f,7); // a(i,j,k)=f(a(i,j,k),7) для каждого элемента a(i,j,k)
b=apply(f,a); // создает новую матрицу с условием b(i,j,k)==f(a(i,j,k))
b=apply(f,a,7); // создает новую матрицу с условием b(i,j,k)==f(a(i,j,k),7)
a.swap_rows(7,9); // переставляет строки a[7] <–> a[9]
Если вы умеете работать с двумерными объектами класса Matrix, то сможете работать и с трехмерными. Например, здесь a — трехмерная матрица, поэтому a[i] — двумерная (при условии, что индекс i не выходит за пределы допустимого диапазона); a[i][j] — одномерная (при условии, что индекс j не выходит за пределы допустимого диапазона); a[i][j][k] — элемент типа int (при условии, что индекс k не выходит за пределы допустимого диапазона).
Поскольку мы видим трехмерный мир, при моделировании чаще используются трехмерные объекты класса Matrix (например, в физическом моделировании в декартовой системе координат).
int grid_nx; // разрешение сетки; задается вначале
int grid_ny;
int grid_nz;
Matrix<double,3> cube(grid_nx, grid_ny, grid_nz);
Если добавить время в качестве четвертого измерения, то получим четырехмерное пространство, в котором необходимы четырехмерные объекты класса Matrix. И так далее.
24.6. Пример: решение систем линейных уравнений
Если вы знаете, какие математические вычисления выражает программа для численных расчетов, то она имеет смысл, а если нет, то код кажется бессмысленным. Если вы знаете основы линейной алгебры, то приведенный ниже пример покажется вам простым; если же нет, то просто полюбуйтесь, как решение из учебника воплощается в программе с минимальной перефразировкой.
Данный пример выбран для того, чтобы продемонстрировать реалистичное и важное использование класса Matrix. Мы решим систему линейных уравнений следующего вида:
a1,1x1 + ... + a1,nxn = b1
...
an,1x1 + ... + an,nxn = bn
где буквы x обозначают n неизвестных, а буквы a и b — константы. Для простоты предполагаем, что неизвестные и константы являются числами с плавающей точкой.
Наша цель — найти неизвестные, которые одновременно удовлетворяют указанные n уравнений. Эти уравнения можно компактно выразить с помощью матрицы и двух векторов.
Ax = b
где A — квадратная матрица n×n коэффициентов:
Векторы x и b векторы неизвестных и константа соответственно.
В зависимости от матрицы A и вектора b эта система может не иметь ни одного решения, одно решение или бесконечно много решений. Существует много разных методов решения линейных систем. Мы используем классическую схему, которая называется исключением Гаусса. Сначала мы преобразовываем матрицу A и вектор b, так что матрица А становится верхней треугольной, т.е. все элементы ниже диагонали равны нулю. Иначе говоря, система выглядит так.
Алгоритм несложен. Для того чтобы элемент в позиции (i,j) стал равным нулю, необходимо умножить строку i на константу, чтобы элемент в позиции (i,j) стал равным другому элементу в столбце j, например a(k, j). После этого просто вычтем одно уравнение из другого и получим a(i,j)==0. При этом все остальные значения в строке i изменятся соответственно.
Если все диагональные элементы окажутся ненулевыми, то система имеет единственное решение, которое можно найти в ходе обратной подстановки. Сначала решим последнее уравнение (это