Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методы вычислений в классах языка С++.doc
Скачиваний:
8
Добавлен:
01.05.2025
Размер:
3.08 Mб
Скачать

2.3. Программная реализация матричного класса

2.3.1. Общее описание структуры матричного класса

Рассмотрим структуру класса для работы с матрицами, алгоритмы для работы с объектами типа «матрица» и примеры методов матричного класса, реализующие эти алгоритмы:

template <class YourOwnFloatType> class matrix

{

Как и векторный, класс для работы с матричными объектами – параметризованный; это всего лишь шаблон, по которому для конкретных типов, подставляемых вместо YourOwnFloatType, компилятор автоматически генерирует класс. Для матричного типа подставляемыми могут быть типы, для которых определены стандартные арифметические операции и перегружены основные операции типа возведения в степень и т.п.

long m, n;

В этих двух приватных переменных мы будем хранить размерность матрицы – число строк (в первой переменной) и столбцов (во второй). Вполне понятным требованием к этим числам является то, что они должны быть натуральными.

vector<YourOwnFloatType> *mtr;

Рассмотрим матрицу

A= .

Каждая строка (также как и столбец) этой матрицы представляет собой упорядоченную последовательность числовых объектов, то есть вектор. Традиционно принято, что векторами являются именно строки матрицы:

A= , , → A= .

Возможность такого представления матрицы и удобство хранения векторов-строк даёт нам основание использовать в качестве внутреннего типа для хранения матрицы указатель на вектор.

public:

Общедоступные методы (функции-члены) и дружественные функции, используемые для управления матричными объектами. Большинство из них являются реализациями соответствующих операций матричной алгебры, а потому и реализованы в виде перегруженных операций.

matrix(char *);

В качестве параметра этот конструктор принимает имя текстового файла, в котором хранится матрица. При этом предполагается, что первые два числа в файле – это размерность матрицы, то есть количество её строк и столбцов. Затем идут числа, составляющие матрицу, количество которых должно быть всего mxn. При считывании числа размещаются построчно: сначала заполняется первая строка, затем – вторая и т.д. Избыток данных в файле игнорируется, при недостатке матрица заполняется нулями, начиная с позиции, следующей за последним числом.

matrix();

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

matrix(long, long);

Этот конструктор принимает два параметра – натуральных числа, первое из которых задаёт количество строк в матрице, а второе – количество столбцов. Так как никаких сведений об элементах матрицы не даётся, считается, что данный конструктор предназначен для создания нулевых матриц заданного размера. Следовательно, при использовании этого конструктора отпадает необходимость очищать матрицу «перед употреблением».

matrix(long, long, YourOwnFloatType *);

Этот конструктор принимает три параметра – натуральных числа, первое из которых задаёт количество строк в матрице, а второе – количество столбцов, и указатель на элементы того типа, который используется для хранения данных в векторах. После создания матрицы заданных размеров, происходит попытка заполнения её size1xsize2 элементами из массива, на который указывает третий параметр. Если этот указатель не инициализирован, или указывает на область памяти, не содержащую объектов подставляемого типа, или количество таких объектов меньше, чем произведение количества строк на количество столбцов, часть или вся матрица, в зависимости от ситуации, будет содержать мусор, над которым и будут производиться дальнейшие операции в предположении о корректности хранящихся данных.

matrix(matrix &);

Конструктор копирования матричного класса используется тогда, когда необходимо слепить матрицу по образу и подобию уже существующей. Это означает, что конструируемая матрица будет иметь ту же размерность, что и копируемая матрица-параметр, а все элементы их будут совпадать. Разумеется, данные этих матриц хранятся в разных областях памяти, и действия над одной матрицей не отражаются на другой, как это происходило бы в синтаксически похожей ситуации объявления ссылочного объекта:

matrix a=е;/*матрицы а и е одинаковые, но в разных областях памяти*/

matrix &c=a;/*матрицы с и а одинаковые и в одной области памяти*/

~matrix();

Деструктор. Если матрица была размещена в оперативной памяти как динамический объект, то он будет вызываться при выполнении оператора delete, во всех остальных – при выходе матричного объекта из области видимости. Так как единственное динамически распределяемое поле нашего класса – это указатель на вектор, то из-под него память и будет освобождена. При этом вызовутся деструкторы каждой вектор-строки, хранящейся в нашей матрице, уничтожая сами данные из строк.

friend matrix<YourOwnFloatType> operator+(matrix <YourOwnFloatType> &, matrix<YourOwnFloatType> &);

Дружественная функция сложения матриц принимает два параметра – матрицы одинакового типа (одинаковой размерности), а матрицу-сумму возвращает как результат. Суммой двух матриц и одинакового типа называется матрица того же типа, элементы которой cij равны суммам соответствующих элементов aij и bij матриц А и B, т. е. ci=aij+bij. Таким образом,

.

С другой стороны, так как в качестве одного из представлений матрицы мы имеем векторное, то сумму матриц достаточно легко представить через сумму векторов, составляющих строки матриц слагаемых:

.

friend matrix<YourOwnFloatType> operator-(matrix <YourOwnFloatType> &, matrix<YourOwnFloatType> &);

Дружественная функция вычитания матриц принимает два параметра – матрицы одинакового типа (одинаковой размерности), а матрицу-разность возвращает как результат. Разность матриц определяется аналогично сумме:

или .

friend matrix<YourOwnFloatType> operator*(matrix <YourOwnFloatType> &, matrix<YourOwnFloatType> &);

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

Пусть и – матрицы типов соответственно mxn и pxq. Если число столбцов матрицы А равно числу строк матрицы B, т. е. n=p, то для этих матриц определена матрица С типа mxq, называемая их произведением:

,

где (i=1, 2, …, m; j=1, 2, …, q).

Из определения вытекает следующее правило умножения матриц: чтобы получить элемент, стоящий в i-й строке и j-м столбце произведения двух матриц, нужно элементы i-й строки первой матрицы умножить на соответствующие элементы j-го столбца второй и полученные произведения сложить.

Произведение АВ имеет смысл тогда и только тогда, когда матрица А содержит в строках столько элементов, сколько элементов имеется в столбцах матрицы В. В частности, можно перемножать квадратные матрицы лишь одинакового порядка. В тех частных случаях, когда АВ=ВА, матрицы А и В называются коммутативными. Так, например, единичная матрица Е коммутативна с любой квадратной матрицей А того же порядка, причем

АЕ=ЕА=А.

Таким образом, единичная матрица Е играет роль единицы при умножении.

friend matrix<YourOwnFloatType> operator* (YourOwnFloatType, matrix<YourOwnFloatType> &);

friend matrix<YourOwnFloatType> operator*(matrix <YourOwnFloatType> &, YourOwnFloatType );

В отличие от умножения матрицы на матрицу, умножение матрицы на скаляр – операция, результат которой – произведение – однозначен и определён всегда: это матрица той же размерности, что и исходная.

Произведением матрицы A на число α (или произведением числа α на матрицу А) называется матрица, элементы которая получены умножением всех элементов матрицы А на число α, т. е.

или .

friend ostream &operator<<(ostream &, matrix<YourOwnFloatType> &);

friend istream &operator>>(istream &, matrix<YourOwnFloatType> &);

Вывод матрицы в поток и ввод матрицы из потока – две операции, классически перегружаемые для каждого типа. Первым параметром каждой из функций есть поток вывода (ввода), вторым – матричный объект, сконструированный ранее. Особый интерес представляет операция ввода из потока: при считывании данных содержимое текущей матрицы замещается объектами, считываемыми из потока – именно с этой целью передача матрицы в функцию организована по ссылке. При записи (вставке) матрицы в поток используется соответствующая перегруженная операция векторного класса для каждого из векторов-строк матрицы.

friend matrix<YourOwnFloatType> SLAE_Orto(matrix <YourOwnFloatType> &, matrix<YourOwnFloatType> &);

Первый параметр этой функции – квадратная матрица NxN, второй – Nx1, возвращаемое значение – матрица Nx1. Рассмотрим детальнее, что это за функция, для чего она предназначена и почему её аргументы именно такие.

Пусть нам необходимо решить систему линейных уравнений вида:

Из элементов этой системы составим три матрицы:

, и .

Умножив матрицу А на матрицу Х, мы получим матрицу-столбец, элементы которой представляют собой левую часть записанной системы. Так как левая часть системы равна правой, то полученная матрица столбец не может быть ничем иным, как матрицей-столбцом В. Таким образом, мы имеем право записать систему линейных алгебраических уравнений в матричной форме как

АХ=В.

В записанной системе матрица-столбец Х представляет собой вектор неизвестных, которые необходимо будет определить, А – матрица коэффициентов при неизвестных и В – правая часть СЛАУ (матрица-столбец свободных членов, взятых с обратным знаком). Итак, задачу решения системы уравнений можно свести к задаче нахождения такой матрицы Х, которая при умножении справа на матрицу А даёт матрицу В. Этим и объясняется требование к параметрам рассматриваемой функции: первая матрица содержит коэффициенты системы уравнений, вторая – столбец свободных членов, взятый с обратным знаком, а возвращаемым значением данной функции будет столбец неизвестных.

Будем исходить из предположения, что такая матрица существует. Тогда домножим это уравнение с обеих сторон слева на некую матрицу А-1 такую, которая при умножении на матрицу А слева даёт единичную матрицу:

А-1АХ=А-1В,

ЕХ=А-1В,

Х=А-1В.

Зная способ получения матрицы А-1 из матрицы А, мы можем решить поставленную задачу. Этот путь мы, однако, рассматривать сейчас не будем, а подойдём к проблеме с другой стороны, для чего рассмотрим метод ортогонализации. Пусть исходная система переписана в виде:

.

Введя обозначение , получим

.

Левую часть каждого уравнения системы можно рассматривать как скалярное произведение двух векторов: и . При такой постановке решение системы сводится к построению вектора, ортогонального к каждому вектору Ai. Добавим к системе векторов Ai линейно независимый от них вектор An+1=(0, 0, …, 0, 1). В векторном пространстве размерности n+1 будем строить такой его ортонормированный базис B1, B2, …, Bn+1, чтобы при любом k=1, 2, …, n+1 векторы B1, B2, …, Bk образовывали ортонормированный базис подпространства Pk, порожденного рассматриваемыми векторами A1, A2, …, Ak. Для этого достаточно строить в подпространстве Pk некоторый ортогональный базис U1, U2, …, Uk, а затем нормировать его.

Вычисления будем вести в соответствии со следующим алгоритмом. Положим:

; .

Если для некоторого k≥1 уже построены векторы U1, U2, …, Uk и векторы B1, B2, …, Bk, то вектор Uk+1 вычисляется с помощью нескольких итераций по формулам

; .

Здесь – начальное, а – очередное приближение вектора Uk+1. Вектор Bk+1 по вектору Uk+1 определяется в соответствии с формулой

.

Значения корней системы связаны с координатами вектора Bn+1 так:

.

Обычно этот процесс рекомендуется повторить 3-4 раза с целью как можно более точной ортогонализации и, соответственно, более точного решения рассматриваемой системы. Более просто рассмотренный алгоритм можно сформулировать так:

  1. Нормируем первую вектор-строку системы.

  2. Ортогонализируем второй вектор к первому, а затем нормируем его.

  3. Ортогонализируем третий вектор к первому и ко второму, а затем нормируем, и т.д., со всеми остальными векторами.

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

friend matrix<YourOwnFloatType> SLAE_Gauss(matrix <YourOwnFloatType> &, matrix<YourOwnFloatType> &);

Для разнообразия рассмотрим ещё один метод решения систем линейных уравнений – метод Гаусса, или метод последовательного исключения неизвестных. Суть его состоит в преобразовании системы

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

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

Пользуясь этим уравнением, легко исключить неизвестное xl из остальных уравнений системы (для этого достаточно из каждого уравнения вычесть его, предварительно умноженное на соответствующий коэффициент при xl). Затем над остальными уравнениями системы совершим аналогичное преобразование: выберем из их числа уравнение с ведущим элементом и исключим с его помощью из остальных уравнений неизвестное х2. Повторяя этот процесс, вместо исходной системы получим равносильную ей систему с треугольной матрицей коэффициентов при неизвестных:

,

из которой последовательно находятся значения всех неизвестных хn, xn-1, …, x1.

Таким образом, процесс решения по методу Гаусса распадается на два этапа. Первый этап, состоящий в последовательном исключении неизвестных, называют прямым ходом. Второй – нахождение значений неизвестных – принято называть обратным ходом.

matrix<YourOwnFloatType> minor(long, long);

Эта функция в программах обычно не применяется и в основном служит для внутреннего употребления. Имеет два параметра – номер строки и столбца данной матрицы, которые не переписываются в матрицу-результат (создание матрицы из имеющейся без заданных строки и столбца). При этом размерность матрицы (количество строк и количество столбцов) уменьшается на единицу.

friend YourOwnFloatType det2(matrix<YourOwnFloatType> &);

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

Определитель (детерминант) порядка n, задаваемый выражением

,

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

.

Это – операция разложения определителя по элементами і-ой строки; – алгебраическое дополнение элемента aij, Mij – минор элемента aij, т.е. определитель (n-1)-го порядка, получаемый из определителя Δ вычёркиванием i-ой строки и j-го столбца.

Описанная процедура позволяет реализовать очень простую рекурсивную функцию для вычисления детерминанта разложением по одной из строк; при этом, однако, число умножений и делений, необходимых для вычисления определителя n-го порядка, равно

,

что сдерживает применение такого метода для вычисления детерминантов высоких порядков.

friend YourOwnFloatType det(matrix<YourOwnFloatType> &);

Для системы уравнений из матрицы коэффициентов мы можем составить детерминант и вычислить его значение. При этом любые изменения матрицы коэффициентов ведут к изменению её детерминанта. Рассмотрим, как в методе Гаусса меняется определителем исходной системы. Обозначим определитель системы уравнений через D. После того, как мы разделим левую и правую части первого уравнения на ведущий элемент a11, определитель преобразованной системы будет равен D/a11, последующие преобразования, связанные с исключением xl из остальных уравнений системы, величину определителя не изменяют.

На втором шаге, когда мы разделим обе части преобразованного второго уравнения на второй ведущий элемент a22, определитель полученной системы будет равен D/(a11·a22). Операции по исключению х2 из уравнений системы вновь не изменяют величины определителя.

Осуществляя аналогичные действия, мы на n-м шаге придем к системе, определитель которой будет равен D/(a11·a22·…·ann). Но матрица коэффициентов при неизвестных системы – треугольная, с единицами на главной диагонали, поэтому ее определитель равен 1. Получаем, что D/(a11·a22·…·ann)=1, то есть D=a11·a22·…·ann.

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

matrix<YourOwnFloatType> operator=(matrix<YourOwnFloatType> &);

Присвоение матриц – бинарная операция, которую реализуют как метод (функцию-член) класса. Если тип (размерность) текущей матрицы не совпадает с размерностью присваиваемой, мы вначале перераспределяем память под её вектора таким образом, чтобы количество векторов и их размерности в обеих матрицах совпали. Затем происходит повекторное копирование строк копируемой матрицы в строки текущей. Возвращаемое значение – копия текущей матрицы.

matrix<YourOwnFloatType> operator*=(matrix<YourOwnFloatType> &x);

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

matrix<YourOwnFloatType> operator+= (matrix<YourOwnFloatType> &x);

matrix<YourOwnFloatType> operator-= (matrix<YourOwnFloatType> &x);

Как и сокращённое умножение, сокращённые операции сложения и вычитания реализованы как методы матричного класса. Обе эти функции работают, используя уже определённые операции сложения, вычитания и присвоения.

matrix<YourOwnFloatType> operator^(long);

Для квадратных матриц мы можем определить такую операцию, как возведение матрицы в целую степень (отрицательные степени при этом не рассматриваются) по следующему закону:

matrix<YourOwnFloatType> operator^=(long);

friend matrix<YourOwnFloatType> pow(matrix<YourOwnFloatType> &, long);

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

matrix<YourOwnFloatType> operator~();

Заменив в матрице типа mxn строки соответственно столбцами, получим так называемую транспонированную матрицу типа nxm.

Транспонированная матрица обладает следующими свойствами:

  1. дважды транспонированная матрица совпадает с исходной;

  2. транспонированная матрица суммы равна сумме транспонированных матриц слагаемых;

  3. транспонированная матрица произведения равна произведению транспонированных матриц сомножителей, взятому в обратном порядке.

Если матрица A квадратная, то det AT=det А. Матрица A называется симметричной, если она совпадает со своей транспонированной, т. е. если AT=А. Отсюда вытекает, что симметричная матрица – квадратная и элементы ее, симметричные относительно главной диагонали, равны между собой. Произведение С=ААТ, очевидно, представляет собой симметричную матрицу, так как СТ=(ААТ)Т=(АТ)ТAТ=AAТ=С.

Рассмотрим систему линейных алгебраических уравнений, записанных в матричной форме, и предположим, что количество уравнений в этой системе превышает количество неизвестных. Разумеется, при использовании, к примеру, метода последовательного исключения неизвестных мы после приведения системы к треугольному виду получим «лишние» уравнения. Если эта система уравнений определяет какую-то зависимость, то точное восстановление этой зависимости становится проблематичным. Однако, используя операцию транспонирования, мы можем получить приблизительные решения этой системы, причём такие, что вычисленная по ним правая часть каждого уравнения будет достаточно мало отличаться от исходной. Запишем эту процедуру в форме матричного уравнения:

AX=B

AТAX=AТB

AТA=A', AТB=B'

A'X=B'

Если решение исходной системы найти было невозможно, то решение преобразованной вполне возможно. Рассмотрим, каковы размерности матриц в последнем уравнении.

Размерность матрицы Аmxn, размерность матрицы ATnxm, размерность матрицы Хnx1, размерность матрицы Вmx1. Размерность произведения AТA, согласно правилу умножения матриц, будет nxn, размерность произведения AТBnx1, что удовлетворяет параметрам функции для решения системы уравнений. Система, определённая таким способом, называется нормальной; матрица этой системы называется матрицей плана, а решение этой системы будет оптимальным по критерию минимизации суммы квадратов отклонений от истинных значений, или, как его называют, критерию МНК – метода наименьших квадратов.

matrix<YourOwnFloatType> operator!();

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

Подставляя конкретные значения в функцию-гипотезу, мы получим систему уравнений, в правой части которой будет линейная комбинация неизвестных коэффициентов функции-гипотезы с числовыми значениями, полученными при подстановке, а в левой – результаты измерений. Если количество уравнений в этой системе у нас будет меньше, чем число коэффициентов в функции-гипотезе, то у нас просто недостаточно данных, чтобы вычислить эти коэффициенты. Если эти значения совпадают или уравнений больше, чем неизвестных, такую систему приводят к нормальной и определяют коэффициенты функции-гипотезы – модели данного процесса. Обозначая через Х матрицу системы, составленной по модельной функции, через Y – матрицу-столбец измеренных значений, а через А – матрицу-столбец неизвестных коэффициентов модели, имеем:

XA=Y – фиктивное матричное уравнение

XTXA=XTY – реальное матричное уравнение

(XTX)A=XTY

A=(XTY)·(XTX)-1

И снова мы встречаемся с особой матрицей, которую обозначили как матрицу в минус первой степени, причём она весьма явно связана с нахождением решения системы уравнений. Это свойство мы используем в следующей функции, а пока рассмотрим понятие обратной матрицы.

Обратной матрицей по отношению к данной называется матрица, которая, будучи умноженной как справа, так и слева на данную матрицу, дает единичную матрицу. Для матрицы А обозначим обратную ей матрицу через А-1. Тогда по определению имеем:

АА-1=А-1А=Е,

где Е – единичная матрица. Нахождение обратной матрицы для данной называется обращением данной матрицы.

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

,

где Aij – алгебраические дополнения соответствующих элементов aij.

Особенная квадратная матрица обратной не имеет, так как её определитель равен нулю.

Укажем некоторые основные свойства обратной матрицы:

  1. Определитель обратной матрицы равен обратной величине определителя исходной матрицы.

  2. Обратная матрица произведения квадратных матриц равна произведению обратных матриц сомножителей, взятому в обратном порядке.

  3. Транспонированная обратная матрица равна обратной от транспонированной данной матрицы.

Определив таким образом обратную для данной матрицу, мы можем решить систему уравнений двумя операциями – обращением и умножением.

matrix<YourOwnFloatType> operator*();

Обращение матрицы описанным выше способом – операция достаточно трудоёмкая. Вспомним, что в процессе решения системы уравнений мы фактически неявно находим обратную матрицу. Для того, чтобы найти её в явном виде, используем следующий приём: запишем вместе квадратную матрицу и единичную. Будем осуществлять такие элементарные преобразования над ними, чтобы привести первую матрицу к диагональной форме. А вторая матрица – будет содержать матрицу, обратную первой.

YourOwnFloatType operator&();

operator YourOwnFloatType();

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

matrix<YourOwnFloatType> operator-();

Унарный минус – операция, из матрицы составляющей противоположную матрицу того же типа: .

matrix<YourOwnFloatType> operator+();

Унарный плюс – операция, приведённая только для полноты набора операций; возвращаемое значение – копия текущей матрицы.

friend long operator==(matrix<YourOwnFloatType> &, matrix <YourOwnFloatType> &);

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

Две матрицы считаются равными, если они одного и того же типа, т.е. имеют одинаковые размерности, и соответствующие их элементы (вектора-строки) равны.

friend long operator!=(matrix<YourOwnFloatType> &, matrix <YourOwnFloatType> &);

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

vector<YourOwnFloatType> &operator[](long a);

Метод матричного класса для индексирования элементов матрицы принимает в качестве параметра номер вектор-строки матрицы, которую необходимо получить. Если этот номер корректен, возвращается ссылка на соответствующий вектор, который, в свою очередь, тоже можно проиндексировать. Таким образом, однократное применение операции индексирования позволяет получить вектор, а двукратное – соответствующий элемент этого вектора. Так как данная операция возвращает ссылочное значение, его можно использовать в операциях присваивания как слева, так и справа. При выходе индекса за допустимый диапазон значений в стандартный поток вывода вставляется специальное диагностическое сообщение и возвращается специальный вектор-признак ошибки.

long getm() { return m; }

long getn() { return n; }

Два служебных метода класса для определения числа строк и числа столбцов.

};