Deutche Knabe
Deutche Knabe

Reputation: 135

How to operate matrices of different size with one function in C?

I have a code from Mathlab, where all matrix operations are done by a couple of symbols. By translating it into C I faced a problem that for every size of matrix I have to create a special function. It's a big code, i will not place it all here but will try to explain how it works.

I also have a big loop where a lot of matrix operations are going on. Functions which are operating with matrices should take matrices as income and store results in temporary matrices for upcoming operations. In fact i know the size of matrices but i also want to make the functions as universal as possible. In oder to reduce code size and save my time.

For example, matrix transposition operation of 2x4 and 4x4 matrices:

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];
        }
    }
}

The operation is simple, but the code is massive already because of 2 different functions, but it will be a slow disaster if I continue like this.

How do i create one function for transposing to operate matrices of different sizes?

I suppose it can be done with pointers, but I don't know how.

I'm looking for a realy general answer to understand how to tune up the "comunication" between functions and temporary matrices, best with an example. Thank you all in advance for the information and help.

Upvotes: 2

Views: 535

Answers (4)

Toby Speight
Toby Speight

Reputation: 30831

You probably don't want to be hard-coding array sizes in your program. I suggest a structure that contains a single flat array, which you can then interpret in two dimensions:

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

Initialize it with

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 */
}

Then, to find the element at position (x,y), we can use a simple function:

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

This has better locality than an array of pointers (and is easier and faster to allocate and deallocate correctly), and is more flexible than an array of arrays (where, as you've found, the inner arrays need a compile-time constant size).

You might be able to use an array of arrays wrapped in a Matrix struct - it's possible you'll need a stride that is not necessarily the same as width, if the array of arrays has padding on your platform.

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 222753

If your C implementation supports variable length arrays, then you can accomplish this with:

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];
}

If your C implementation does not support variable length arrays, but does allow pointers to arrays to be cast to pointers to elements and used to access a two-dimensional array as if it were one-dimensional (this is not standard C but may be supported by a compiler), you can use:

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];
}

The above requires the caller to cast the arguments to float *. We can make it more convenient for the caller with:

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];
}

(Unfortunately, this prevents the compiler from checking that the argument types match the intended types, but this is a shortcoming of C.)

If you need a solution strictly in standard C without variable length arrays, then, technically, the proper way is to copy the bytes of the objects:

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));
    }
}

Notes:

Include <stdlib.h> to declare size_t and, if using the last solution, include <string.h> to declare memcpy.

Variable length arrays were required in C 1999 but made optional in C 2011. Good quality compilers for general purpose systems will support them.

Upvotes: 1

H.S.
H.S.

Reputation: 12669

If you are using C99 compiler, you can make use of Variable Length Array (VLA's) (optional in C11 compiler). You can write a function like this:

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];
        }
    }
}

This one function can work for the different number of rows_in_matrix and columnes_in_matrix. Call it like this:

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

Upvotes: 1

user2736738
user2736738

Reputation: 30926

There are different way you can achieve this in from not so good to good solutions.

If you know what the maximum size of the matrices would be you can create a matrix big enough to accommodate that size and work on it. If it is lesser than that - no problem write custom operations only considering that small sub-matrix rather than the whole one.

Another solution is to - create a data structure to hold the matrix this may vary from jagged array creation which can be done using the attribute that is stored in the structure itself. For example: number of rows and column information will be stored in the structure itself. Jagged array gives you the benefit that now you can allocate de-allocate memory - giving you a better control over the form - order of the matrices. This is better in that - now you can pass two matrices of different sizes and the functions all see that structure which contain the actual matrix and work on it. (wrapped I would say). By Structure I meant something like

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

Upvotes: 2

Related Questions