Intereting Posts
Как fork () знает, находится ли он в дочернем процессе и в родительском процессе? Удаление узла из связанного списка по индексу Как написать отдельные биты в файл в C printf с непревзойденным форматом и параметрами Понятие ключевого слова «auto» в c Переменная, объявленная и инициализируемая в инструкции switch Один большой malloc против нескольких меньших реаллоков Связывание с библиотекой в ​​зависимости от сторонних библиотек Получить символ, на который ссылается индекс в строке C Как вы можете отслеживать ввод мыши с использованием наименьших библиотек в c Как собрать поплавок из двух байтов? Ошибка: неизвестное имя типа Список при попытке создать связанный список Есть ли случай, когда включение одного и того же заголовка в два раза действительно полезно? Необработанный указатель превращается в нуль, проходящий от Rust до C Как можно использовать двойной указатель для двумерной матрицы?

Наиболее эффективный способ получения столбцов многомерного массива в C

Я пытаюсь создать матричную структуру данных в C. У меня есть структура и есть двумерный векторный указательный массив (размер динамически определяется в куче) для части груза (данных) в этой структуре.

Учитывая индекс столбца, я хочу получить значения этого столбца в одномерном массиве. Легко это с одним циклом для или while. Но если количество строк в этой матрице равно N, то для получения вектора столбца потребуется время O (N). Могу ли я сделать это более эффективно с операциями памяти, такими как memcpy и как? В противном случае, как я могу улучшить производительность (мои данные довольно структурированы, и мне нужно сохранить это в какой-то матрице).

Если вы хотите скопировать данные в свою матрицу, вы не сможете сделать это менее чем за время O (N), будь то строка или столбец, за исключением небольшого N, где могут оказаться аппаратные функции.

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

Код ниже вводится прямо в текстовое поле ответа и даже не был скомпилирован. Используйте на свой риск!

Тип матрицы определяется как структура таким образом:

typedef struct { unsigned int refCount; // how many Matrixes are referencing this data ref size_t lineWidth; // number of doubles between element at row = n, col = 0 and row = n +1, col = 0 double* data; // the actual data } DataRef; typedef struct { size_t rows; // num rows in matrix size_t cols; // num cols in matrix size_t dataOffset; // offset in doubles from the start of data of element at row = 0, col = 0 DataRef* data; } Matrix; 

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

 Matrix* matrix_create(size_t rows, size_t cols, const double* values) { Matrix* ret = calloc(1, sizeof *ret); ret->rows = rows; ret->cols = cols; ret->dataOffset = 0; ret->data = calloc(1, sizeof *dataRef); ret->data->lineWidth = cols; ret->data->data = allocateAndCopy(rows * cols, values); // mallocs a new block of doubles big enough for the values ret->data->refCount = 1; return ret; } 

Для доступа к элементу (опять-таки нет обработки ошибок, например ошибок границ)

 double matrix_elementAt(Matrix* matrix, size_t row, size_t col) { size_t offset = matrix->dataOffset + row * matrix->data->lineWidth + col; return *(matrix->data->data + offset); } 

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

 Matrix* matrix_createFromRegion(Matrix* old, size_t startRow, size_t startCol, size_t rows, size_t cols) { Matrix* ret = calloc(1, sizeof *ret); ret->rows = rows; ret->cols = cols; ret->dataOffset = old->dataOffset + startRow * old->dataLineWidth + startCol; ret->data = old->data; ret->data->refCount++; return ret; } 

Чтобы создать новую матрицу из столбца в другой матрице:

 Matrix* vector = matrix_createFromRegion(aMatrix, 0, colYouWant, matrix_numRows(aMatrix), 1); 

Чтобы освободить матрицу

 void matrix_free(Matrix* aMatrix) { if (aMatrix->data->refCount == 1) { free(aMatrix->data->data); free(aMatrix->data); } else { aMatrix->data->refCount--; } free(aMatrix); } 

Если вы хотите изменить переменные матрицы, в любое время, когда вы изменяете элемент, проверьте refCount, и если он больше 1, скопируйте DataRef перед его модификацией (уменьшите refCount на старом dataRef), иначе измените значение dataRef.

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

Если количество строк в столбце равно N, вы не можете копировать, читать или иным образом манипулировать всем столбцом за меньшее время, чем O (N). Это прочная нижняя граница; каждый элемент должен быть рассмотрен, и есть N из них.

Нет, вы не можете сделать это быстрее, чем O (N).

Обратите внимание, что x[3][5] переводится компилятором в x+((3*num_cols)+5)*size_of_element для 2D-массивов известного размера. Одним из способов сделать ваш массив быстрее было бы, таким образом, удалить его динамический размер.

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

Как говорит Бореалид, вы не можете улучшить O (N). Тем не менее, вы можете ускорить операцию копирования, если вы переупорядочиваете свои данные, чтобы строки были столбцами и столбцами. Это позволит вам использовать memcpy для дублирования данных.

Мое решение:

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

  2. Теперь вы можете настроить указатель src указывающий на первый элемент столбца ( src = &matrix[row*ncols+col]; ), и скопировать столбец с помощью: for (i=0; i