如何在C中使用一个函数操作不同大小的矩阵?

时间:2018-02-27 11:57:50

标签: c arrays pointers matrix

我有一个来自Mathlab的代码,其中所有矩阵运算都由几个符号完成。通过将其转换为C,我遇到了一个问题,即对于每种尺寸的矩阵,我都必须创建一个特殊的函数。这是一个很大的代码,我不会把它放在这里,但会尝试解释它是如何工作的。

我还有一个很大的循环,其中正在进行大量的矩阵运算。使用矩阵运算的函数应将矩阵作为收入,并将结果存储在临时矩阵中,以用于即将进行的操作。事实上,我知道矩阵的大小,但我也希望尽可能使函数成为通用。在奥德,以减少代码大小,节省我的时间。

例如,2x4和4x4矩阵的矩阵转置操作:

void A_matrix_transposition (float transposed_matrix[4][2], float matrix[2][4], int rows_in_matrix, int columnes_in_matrix);
void B_matrix_transposition (float transposed_matrix[4][4], float matrix[4][4], int rows_in_matrix, int columnes_in_matrix);

int main() {

float transposed_matrix_A[4][2]; //temporary matrices
float transposed_matrix_B[4][4];

float input_matrix_A[2][4], input_matrix_B[4][4]; //input matrices with numbers

A_matrix_transposition (transposed_matrix_A, input_matrix_A, 2, 4);
B_matrix_transposition (transposed_matrix_B, input_matrix_B, 4, 4);
// after calling the functions i want to use temporary matrices again. How do I pass them to other functions if i dont know their size, in general?
}

void A_matrix_transposition (float transposed_matrix[4][2], float matrix[2][4], int rows_in_matrix, int columnes_in_matrix)
{ static int i,j;
        for(i = 0; i < rows_in_matrix; ++i) {
        for(j = 0; j < columnes_in_matrix; ++j)
        { transposed_matrix[j][i] = matrix[i][j];
        }
    }
}

void B_matrix_transposition (float transposed_matrix[4][4], float matrix[4][4], int rows_in_matrix, int columnes_in_matrix)
{ static int i,j;
        for(i = 0; i < rows_in_matrix; ++i) {
        for(j = 0; j < columnes_in_matrix; ++j)
        { transposed_matrix[j][i] = matrix[i][j];
        }
    }
}

操作很简单,但由于2种不同的功能,代码已经庞大了,但如果我继续这样做,那将是一场缓慢的灾难。

如何创建一个用于转置操作不同大小矩阵的函数?

我想可以用指针完成,但我不知道如何。

我正在寻找一个真正的一般答案来理解如何调整函数和临时矩阵之间的“通信”,最好用一个例子。提前感谢大家提供的信息和帮助。

4 个答案:

答案 0 :(得分:2)

中,从不太好的解决方案到良好的解决方案,您可以通过不同的方式实现这一目标。

如果您知道矩阵的最大大小是什么,您可以创建一个足够大的矩阵来适应该大小并对其进行处理。如果它小于那个 - 没有问题只考虑小的子矩阵而不是整个矩阵来编写自定义操作。

另一种解决方案是 - 创建一个数据结构来保存矩阵,这可能与锯齿状数组创建不同,这可以使用存储在结构本身中的属性来完成。例如:行数和列信息将存储在结构本身中。 Jagged数组为您提供了好处,现在您可以分配解除分配内存 - 让您更好地控制表单 - 矩阵的顺序。这样做更好 - 现在你可以传递两个不同大小的矩阵,所有函数都看到包含实际矩阵的结构并对其进行处理。 (包裹我会说)。 按结构我的意思是

struct matrix{
   int ** mat;
   int row;
   int col;
}

答案 1 :(得分:1)

如果您的C实现支持可变长度数组,那么您可以通过以下方式完成此任务:

void matrix_transposition(size_t M, size_t N,
    float Destination[M][N], const float Source[N][M])
{
    for (size_t m = 0; m < M; ++m)
    for (size_t n = 0; n < N; ++n)
        Destination[m][n] = Source[n][m];
}

如果您的C实现不支持可变长度数组,但允许指向数组的指针转换为指向元素的指针并用于访问二维数组,就像它是一维的一样(这不是标准C但是可能由编译器支持),您可以使用:

void matrix_transposition(size_t M, size_t N,
    float *Destination, const float *Source)
{
    for (size_t m = 0; m < M; ++m)
    for (size_t n = 0; n < N; ++n)
        Destination[m*N+n] = Source[n*M+m];
}

以上要求调用者将参数强制转换为float *。我们可以通过以下方式使呼叫者更方便:

void matrix_transposition(size_t M, size_t N,
    void *DestinationPointer, const void *SourcePointer)
{
    float *Destination = DestinationPointer;
    const float *Source = SourcePointer;

    for (size_t m = 0; m < M; ++m)
    for (size_t n = 0; n < N; ++n)
        Destination[m*N+n] = Source[n*M+m];
}

(不幸的是,这会阻止编译器检查参数类型是否与预期类型匹配,但这是C的缺点。)

如果您需要严格使用标准C的解决方案而没有可变长度数组,那么从技术上讲,正确的方法是复制对象的字节:

void matrix_transposition(size_t M, size_t N,
    void *DestinationPointer, const void *SourcePointer)
{
    char *Destination = DestinationPointer;
    const char *Source = SourcePointer;

    for (size_t m = 0; m < M; ++m)
    for (size_t n = 0; n < N; ++n)
    {
        // Calculate locations of elements in memory.
        char *D = Destination + (m*N+n) * sizeof(float);
        const char *S = Source + (n*M+m) * sizeof(float);
        memcpy(D, S, sizeof(float));
    }
}

注意:

包含<stdlib.h>以声明size_t,如果使用上一个解决方案,请添加<string.h>来声明memcpy

可变长度阵列在C 1999中是必需的,但在C 2011中是可选的。用于通用系统的高质量编译器将支持它们。

答案 2 :(得分:1)

如果您使用的是C99编译器,则可以使用可变长度数组(VLA&#39; s)(在C11编译器中可选)。你可以写一个这样的函数:

void matrix_transposition (int rows_in_matrix, int columnes_in_matrix, float transposed_matrix[columnes_in_matrix][rows_in_matrix], float matrix[rows_in_matrix][columnes_in_matrix])
{ 
    int i,j;
    for(i = 0; i < rows_in_matrix; ++i) {
        for(j = 0; j < columnes_in_matrix; ++j)
        { 
            transposed_matrix[j][i] = matrix[i][j];
        }
    }
}

此功能适用于rows_in_matrixcolumnes_in_matrix的不同数量。这样称呼:

matrix_transposition (2, 4, transposed_matrix_A, input_matrix_A);
matrix_transposition (4, 4, transposed_matrix_B, input_matrix_B);

答案 3 :(得分:1)

您可能不希望在程序中对数组大小进行硬编码。我建议一个包含单个平面数组的结构,然后您可以在两个维度中进行解释:

typedef struct {
    size_t width;
    size_t height;
    float *elements;
} Matrix;

使用

初始化它
int matrix_init(Matrix *m, size_t w, size_t h)
{
    m.elements = malloc((sizeof *m.elements) * w * h);
    if (!m.elements) {
        m.width = m.height = 0;
        return 0; /* failed */
    }
    m.width = w;
    m.height = h;
    return 1; /* success */
}

然后,为了找到位置(x,y)的元素,我们可以使用一个简单的函数:

float *matrix_element(Matrix *m, size_t x, size_t y)
{
    /* optional: range checking here */
    return m.elements + x + m.width * y;
}

这比指针数组具有更好的局部性(并且更容易和更快地分配和解除分配),并且比数组数组更灵活(正如您所发现的,内部数组需要编译 - 时间常数)。

您可以使用包含在Matrix结构中的数组数组 - 您可能需要一个与stride不一定相同的width,如果数组数组在您的平台上有填充。