我正在尝试用C创建一个矩阵数据结构。我有一个结构,并且在这个结构中有货物部分(数据)的二维空指针数组(大小是在堆上动态定义的)。
给定列索引,我想在一维数组中获取此列的值。使用for或while循环很容易实现。但是如果此矩阵中的行数为N,那么获取列向量需要花费O(N)时间。我可以通过memcpy等内存操作更有效地完成这项工作吗?否则我怎样才能提高性能(我的数据非常结构化,我需要将它存储在某种矩阵中)。
答案 0 :(得分:4)
如果列中的行数为N,则无法在比O(N)更短的时间内复制,读取或以其他方式操作整列。这是一个坚定的下限;必须考虑每个元素,并且有N个。
所以不,你不能比O(N)更快。
请注意,对于已知大小的2D数组,编译器将x[3][5]
翻译为x+((3*num_cols)+5)*size_of_element
。因此,提高阵列速度的一种方法是删除其动态大小。
另一个重要的一点是顺序访问内存并不总是最快 - 所以只需将数组旋转90度就不一定能给你带来最好的效果。查看阻止作为优化技术。底线:最佳内存布局取决于您的访问模式和硬件参数,如缓存行长度和缓存大小。
答案 1 :(得分:1)
正如Borealid所说,你无法改善O(N)。但是,如果重新排序数据,则可以加快复制操作,以便行是列,列是行。这将允许您使用memcpy复制数据。
答案 2 :(得分:1)
我的解决方案:
不要使用多维数组。它们在C99之前是不灵活的(不能改变所有尺寸)并且排除了如下的高效操作。相反,只需使用一维数组并自己进行元素索引算法。
现在,您可以设置指向列的第一个元素src
)的指针src = &matrix[row*ncols+col];
,并使用以下代码复制列:for (i=0; i<nrows; i++, src+=ncols) dest[i] = *src;
答案 3 :(得分:1)
如果要复制矩阵中的数据,无论是行还是列,都不能在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(减去旧dataRef上的refCount),否则修改dataRef。
现在上面使用了大量的mallocs,因此效率可能低于小矩阵的朴素实现。但是,您可以维护一个未使用的DataRef结构和Matrix结构的列表,而不是在完成后释放它们,将它们放在空闲列表中。分配新的结构时,从空闲列表中获取结构,除非它们是空的。这样,获得表示现有矩阵列的矩阵通常会花费不变的时间。