Просто случайность, хотя здесь (нет опыта, насколько хорошо это действительно работает, и без кофе поздно ночью):
Я думаю, что внутренняя часть матрицы должна быть хеш-таблицей, а не массивом.
Каждая ячейка в массиве имеет три элемента информации:
- Строка, в которой находится ячейка
- Столбец, в котором находится ячейка
- Значение ячейки
На мой взгляд, это легко представить кортежем ((i, j), v)
, где (i, j)
обозначает положение ячейки (i-я строка, j-й столбец), а v
Это было бы несколько нормальное представление матрицы. Но давайте отвлечемся от идей здесь. Вместо i
, обозначающего строку как позицию (т. Е. От 0 до 1 до 2 до 3 и т. Д.), Давайте просто рассмотрим i
как своего рода канонический идентификатор для соответствующей ей строки. Давайте сделаем то же самое для j
. (Хотя в самом общем случае i
и j
могут быть неограниченными, давайте предположим простой случай, когда они останутся в пределах [0..M] и [0..N] для M x N матрицы, но не обозначают фактические координаты ячейки).
Теперь нам нужен способ отслеживать идентификатор строки и текущий индекс, связанный со строкой. Это явно требует структуры данных ключ / значение, но поскольку число индексов является фиксированным (матрицы обычно не увеличиваются / уменьшаются) и имеет дело только с интегральными индексами, мы можем реализовать это как фиксированный одномерный массив. Для матрицы из M строк мы можем иметь (в C):
int RowMap[M];
Для m-й строки RowMap[m]
дает идентификатор строки в текущей матрице.
Мы будем использовать то же самое для столбцов:
int ColumnMap[N];
где ColumnMap[n]
- идентификатор n-го столбца.
Теперь вернемся к хеш-таблице, которую я упоминал в начале:
Поскольку у нас есть полная информация (размер матрицы), мы должны быть в состоянии генерировать идеальную функцию хеширования (без коллизий). Вот одна из возможностей (для массивов небольшого размера):
int Hash(int row, int column)
{
return row * N + column;
}
Если это хеш-функция для хеш-таблицы, мы должны получить нулевые коллизии для большинства размеров массивов. Это позволяет нам читать / записывать данные из хеш-таблицы за O (1) раз.
Классная часть связывает индекс каждой строки / столбца с идентификаторами в хеш-таблице:
// row and column are given in the usual way, in the range [0..M] and [0..N]
// These parameters are really just used as handles to the internal row and
// column indices
int MatrixLookup(int row, int column)
{
// Get the canonical identifiers of the row and column, and hash them.
int canonicalRow = RowMap[row];
int canonicalColumn = ColumnMap[column];
int hashCode = Hash(canonicalRow, canonicalColumn);
return HashTableLookup(hashCode);
}
Теперь, поскольку интерфейс к матрице использует только эти дескрипторы, а не внутренние идентификаторы, операция swap
строк или столбцов соответствует простому изменению в массиве RowMap
или ColumnMap
:
// This function simply swaps the values at
// RowMap[row1] and RowMap[row2]
void MatrixSwapRow(int row1, int row2)
{
int canonicalRow1 = RowMap[row1];
int canonicalRow2 = RowMap[row2];
RowMap[row1] = canonicalRow2
RowMap[row2] = canonicalRow1;
}
// This function simply swaps the values at
// ColumnMap[row1] and ColumnMap[row2]
void MatrixSwapColumn(int column1, int column2)
{
int canonicalColumn1 = ColumnMap[column1];
int canonicalColumn2 = ColumnMap[column2];
ColumnMap[row1] = canonicalColumn2
ColumnMap[row2] = canonicalColumn1;
}
Так и должно быть - матрица с O (1) доступом и мутацией, а также O (1) перестановкой строк и O (1) перестановкой столбцов. Конечно, даже O (1) хеш-доступ будет медленнее, чем O (1) доступа на основе массива, и будет использоваться больше памяти, но по крайней мере существует равенство между строками / столбцами.
Я старался быть как можно более агностиком, когда дело доходит до того, как именно вы реализуете свою матрицу, поэтому я написал несколько C. Если вы предпочитаете другой язык, я могу изменить его (было бы лучше, если бы вы поняли), но я думаю, что это довольно информативно, хотя я не могу гарантировать, что это исправление в отношении C, так как я на самом деле парни из C ++, пытающиеся вести себя как парни из C прямо сейчас (и я упоминал, что у меня нет кофе ?). Лично, написание на полном ОО-языке сделало бы это более привлекательным, а также придало бы коду некоторую прелесть, но, как я уже сказал, это было быстрое внедрение.