В данном задании реализованы следующие классы и методы работы с ними
- DenseMatrix - плотная матрица;
- SparseMatrix - разреженная матрица;
- Matrix - единый интерфейс для работы с матрицами разного типа;
- RowIterator и ColumnIterator - интерфейс, позволяющий итерироваться по элементам матрицы;
- Graph - граф, который представляется в виде матрицы смежности. В зависимости от задачи и плотности графа (количества рёбер) можно использовать различные реализации матрицы (разреженная или плотная).
В классе Graph реализуются две прикладные задачи на графах: ● вычислить диаметр графа; ● вычислить количество связных компонент графа.
- SparseMatrix хранится в формате CSR. Доступ к элементу через двойные скобки и разыменование через прокси-классы. Подробнее в ReadMeSparse.txt;
- DenseMatrix хранится в формате вектор векторов.
Квадратная матрица смежности. Может иметь тип SparseMatrix либо DenseMatrix. Тип
матрицы выбирается при создании графа
Флаг, обозначающий, ориентированный ли граф или нет.
Утилита, используется при реализации метода double Diameter().
Утилита, используется при реализации метода double Diameter().
Утилита, используется при реализации метода double Diameter().
Конструктор. Создание графа с помощью квадртаной матрицы смежности. При попытке
создать граф с помощью не квадратной матрицы генерируется исключение. При вызове
конструктора автоматически вызывается метод isOriented(), определяющий,
является ли граф ориентированным (т.е. симметрична ли матрица относительно
главной диагонали).
Конструктор копирования.
Обычное копирование.
Из ориентированного графа делает неориентированный с помощью отражения ненулевых
элементов относительно главной диагонали. Если существует путь из одной вершины в
другую, вес которого не равен весу пути из другой вершины в первую, то
предпочтение отдается весу, расположенному над главной диагональю. Пример:
\ 0 1 2 \ 0 1 2
0| 0 1 0 0| 0 1 0
1| 3 0 1 => 1| 1 0 1
2| 0 0 0 2| 0 1 0
Функция, возвращающая количество связных компонент графа.
! В реализации метода используются итераторы RowIterator, позволяющие
итерироваться по ненулевым элементам матрицы смежности.
Функция, возвращающая 1 если граф ориентированный (матрица смежности не
симметрична относительно главной диагонали), и 0, если граф ориентированный.
Функция, возвращающая диаметр графа. При реализации использовался алгоритм
Дейкстры. При попытке вычислить диаметр для несвязного, ориентированного графа
или графа, содержащего отрицательные веса путей, генерируется исключение.
! В реализации метода используются итераторы RowIterator, позволяющие
итерироваться по ненулевым элементам матрицы смежности.
Функция-утилита, печатающая матрицу смежности.
Функция, добавляющая путь из вершины x в вершину y с весом val.
Интерфейс для работы с матрицами типа Sparse или Dense.
Метод возвращает ссылку на элемент матрицы Aij.
double get(size_t, size_t) -- получение значения по индексам (работает и для
константных объектов)
void set(size_t, size_t, double) -- изменение значения по индексам
size_t num_rows() -- количество строк (работает и для константных объектов)
size_t num_columns() -- количество строк (работает и для константных объектов)
- операции сравнения двух матриц (==, !=)
- бинарная операция +, сложение разряженных матриц
- бинарная операция *, умножение * умножение матриц (mult)
- операция индексации [i]
- операции для поддержки адресной арифметики
- операция вывода >>
Принцип работы с итераторам такой же, как и в стандартной библиотеке C++
- RowIterator iterate_rows(size_t i); // Создание итератора по строкам в i-м столбце
- RowIterator end_rows(size_t i); // Последний элемент в i-м столбце, аналог end()
- ColumnIterator iterate_columns(size_t i); // Создание итератора по столбцам i-м строке
- ColumnIterator end_columns(size_t i); // Последний элемент в i-й строке, аналог end()
Хранение матрицы в формате CSR: В составе класса 3 динамических массива: - data - массив значений. Хранит значения ненулевых элементов, взятые подряд из первой непустой строки, затем идут значения из следующей непустой строки и т.д.; - col - массив индексов столбцов. Размер равен размеру массива data, хранит номера столбцов, соответствующих элементов из массива значений. - row массив индексации строк - массив размера n + 1, где n - кол-во строк в матрице. Для индекса i хранит количество ненулевых элементов в строках до i − 1 включительно. Последний элемент массива индексации строк совпадает с кол-вом ненулевых элементов (размером массива data и col) а первый всегда равен 0.
Для того, чтобы посмотреть содержимое matrix[i][j] (индексация с 0), нужно:
0. Если row[i+1] - row[i] = 0, то в строке нет ненулевых элементов.
1. row[i] - индекс первого ненулевого элемента i-й строки в масиве data. То есть
matrix[i][col[row[i]]]- первый ненулевой элемент iй строки, а data[row[i]] - его
значение.
2. Итак, в i-й строке всего row[i+1] - row[i] ненулевых элементов. Смотрим
содержимое массива col и ищем значение j. Если в диапазоне от col[row[i]] до
col[row[i+1]] такого значения нет, то matrix[i][j] = 0, иначе
matrix[i][j] = data[k], где k: col[k] = j и row[i] <= k < row[i+1]
Примеры:
data {1, 2, 3, 4, 5} |1 |
col {0, 0, 2, 1, 3} |2 3 |
row {0, 1, 3, 5} | 4 5|
data {1, 6!, 2, 3, 4, 5} |1 6 |
col {0, 2!, 0, 2, 1, 3} |2 3 |
row {0, 1+1, 3+1, 5+1} | 4 5|
data {1, 2, 6!, 3, 4, 5} |1 6 |
col {0, 0, 1!, 2, 1, 3} |2 6 3 |
row {0, 1, 3+1, 5+1} | 4 5|
The number of elements in a column №r equals row[r] - row[r-1]
В составе класса также есть 2 подкласса Row и constRow, которые, в свою очередь,
имеют свои подклассы subRow и constsubRow. Данные классы используются для
перегрузки операторов разыменования и индексации.
- int num_columns; // кол-во столбцов в матрице
- int num_rows; // кол-во строк в матрице
- double *data; // массив значений, динамический
- int *row; // массив индексации строк, динамический
- int *col; // массив индексации столбцов
- int non_zero; // размер динамических массивов data и col
При создании матрица инициализирована нулями. Начальная длина динамических
массивов (rows*columns + 10) / 5. По умолчанию количество строк и столбцов равно
константному числу N.
Метод получает на вход строку и столбец искомого элемента, при его отсутсвии
создает ячейку в массиве data и col, и возвращает адрес ячейки в массиве data.
Алгоритм:
1. Look for a col[i] == c in range from row[r] to row[r+1] - 1
2. If there is nothing and i == row[r+1], shift everything right
3. If col[i-1] < c < col[i], shift everythinf right
4. return pointer to the element in data array
Метод убирает нулевые значения (точнее значения по модулю меньшие эпсилон) из
массива значений data, сдвигает остальные элементы массива влева. Аналогичым
образом меняется массив col. В массиве row нужные элементы уменьшаются на 0.
Ситуация возникновения нулей в массиве может быть обусловлена выражениями типа:
A[i][j] = 0;
*(*(A + i) + j) = 0;
т.к. операция присваивания не перегружена, и пользователь можнт по ошибке
добавить в матрицу ноль.
Хранение матрицы в виде вектора из векторов.
- int num_columns; // кол-во столбцов в матрице
- int num_rows; // кол-во строк в матрице
- std::vector<std::vector> *data; // массив значений -- количество строк (работает и для константных объектов) size_t num_columns() -- количество строк (работает и для константных объектов)
- Makefile - файл-сборщик
- graph.hpp - библиотека для работы c графами graph.cpp
- matrix.hpp - библиотека для работы с матрицами
- densematrix.cpp - библиотека для работы с плотными матрицами (подкласс матриц) densematrix.hpp
- sparsematrix.hpp - библиотека для работы с разреженными матрицами (подкласс матриц) sparsematrix.cpp
- iterator.hpp - библиотека для работы с итераторами iterator.cpp
- main.cpp - основная программа, содержит пример использования графов и матриц