简体   繁体   中英

C program to calculate matrix multiplication

I'm trying to write a C program that will calculate M N where M is a square matrix and N is an integer. Every time after I enter a value for N, however, it crashes. Would anyone be able to identify what's wrong with it? I would presume it's something with regards to pointers but I do not understand what changes need to be made to them. Any assistance would be much appreciated.

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

double *multiply(int M, double **A, double **B) {
    double **C = (double **)malloc(M * sizeof(double));

    for (int i = 0; i < M; i++) {
        C[i] = (double *)malloc(M * sizeof(double));
    }

    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            for (int k = 0; k < M; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }

    return *C;
}

void arrcpy(int M, double **A, double **B) {
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            A[i][j] = B[i][j];
        }
    }
    return;
}

int main() {
    int i, j, N, M;

    printf("M = ");
    scanf("%d", &M);

    double **R = (double **)malloc(M * sizeof(double));
    for (i = 0; i < M; i++) {
        R[i] = (double *)malloc(M * sizeof(double));
    }

    for (i = 0; i < M; i++) {
        for (j = 0; j < M; j++) {
            printf("R[%d][%d] = ", i, j);
            scanf("%lf", &R[i][j]);
        }
    }

    printf("N = ");
    scanf("%d", &N);

    double **C;
    arrcpy(M, C, R);
    for (i = 1; i < N; i++) {
        *C = multiply(M, C, R);
    }

    for (i = 0; i < M; i++) {
        for (j = 0; j < M; j++) {
            printf("%lf ", C[i][j]);
        }
        printf("\n");
    }

    return 0;
}

The answer from @tshiono already points out a number of problems in OPs code like C being used uninitialized, wrong dynamic allocation, etc.

This answer will suggest an alternative method for memory allocation.

The approach used by OP is an array of M pointers where each pointer points to an array of M double. This approach requires that the array of M pointers is first allocated by one malloc call and then a loop with a malloc for each array of double. That is M+1 malloc calls for a single matrix.

This can be simplified to a single malloc by declaring the matrix variable as a pointer to an array of M double . Like

double (*R)[M] = malloc(M * sizeof *R);
\------------/   \-------------------/
 R is a pointer   Allocate M x M doubles
 to an array      in a single malloc call
 of M double

Likewise, freeing an object of this type will only require one call of free instead of M+1 calls.

Another benefit is that the matrix will be stored in one contiguous memory block. This allows for using memcpy for copying a matrix instead of writing a custom function.

This could look like:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void multiply(int M, double (*restrict result)[M], double (*A)[M], double (*B)[M])
{
  for(int i=0; i<M; i++) {
    for(int j=0; j<M; j++) {
      result[i][j] = 0;
      for(int k=0; k<M; k++) {
        result[i][j] += A[i][k] * B[k][j];
      }
    }
  }
}

int main()
{
    int i, j, N, M;

    printf("M = ");
    if (scanf("%d",&M) != 1) exit(1);

    double (*R)[M] = malloc(M * sizeof *R);
    double (*C)[M] = malloc(M * sizeof *C);
    double (*T)[M] = malloc(M * sizeof *T);
    if (R == NULL || C == NULL || T == NULL) exit(1);

    for(i=0; i<M; i++) {
        for(j=0; j<M; j++) {
            printf("R[%d][%d] = ", i, j);
            if (scanf("%lf", &R[i][j]) != 1) exit(1);
        }
    }

    printf("N = ");
    if (scanf("%d",&N) != 1) exit(1);

    memcpy(C, R, M * sizeof *R);

    for(i = 1; i < N; i++) {
      multiply(M, T, C, R);
      memcpy(C, T, M * sizeof *R);
    }

    for(i = 0; i < M; i++) {
        for(j = 0; j < M; j++) {
            printf("%lf ", C[i][j]);
        }
        printf("\n");
    }

    free(R);
    free(C);
    free(T);

    return 0;
}

In the code above it's a bit annoying that the result of the multiplication ends in T and then it's copied to C for the next loop iteration. We can avoid that by changing:

    memcpy(C, R, M * sizeof *R);

    for(i = 1; i < N; i++) {
      multiply(M, T, C, R);
      memcpy(C, T, M * sizeof *R);
    }

to

    // Make sure that an even number of multiplications is needed after this block
    if (N > 1 && (N & 1) == 0) {
      multiply(M, C, R, R);
      --N;
    } else {
      memcpy(C, R, M * sizeof *R);
    }

    // Do two multiplications in each loop
    for(i = 1; i < N; i += 2) {
      multiply(M, T, C, R);
      multiply(M, C, T, R);
    }

A bit more complex but it avoids a memcpy inside the loop.

In my opinion, the best approach is to implement the fast exponentiation algorithm , illustrated in the code below.

The code shown allows you to use a special type of matrix, that just requires one malloc() call per matrix (I will not bother you with the problems in your code that has already been answered for in other answers) The matrix (I provided m_rows and m_cols fields to be able to hold rectangular matrices, while the implementation focuses on the problem to solve, which is matrix power raising) is stored in a single block of memory that includes space for the struct matrix itself, the pointers for the array of rows, and the rows themselves (there should be no alignment issues, as all blocks have been addressed using pointer arithmetic properly). It initializes the pointers to the proper places by moving two pointers of appropiate types (one double pointer for moving to the next cell, to initialize cells, and other double * to move to the next row pointer, for initializing the pointers to the rows) The set of provider functions (all the ones prefixed with prov_ ) allow you to initialize the matrix in too many different ways, by using the same routine. If NULL is provided as provider, then no cell initialization is done (eg in matrix multiplication, in which initialization is done by the product routine). Study it as it is very fast.

I have implemented the matrix create routines (a single free() call to the returned value is enough to free the whole matrix) and operations for adding, initializing in several flavours (from a file, from an array, from a FILE * stream, ... some of them are not used in the program, but are presented to illustrate usage hints anyway) so you will probably use it as a reference implementation. All the routines reachable from main() have been tested, and the sample code uses a sample matrix and tests that the characteristical polynomial, when applied to the matrix, results in the zero matrix. For the test case, the approach of using fast exponentiation is of no use, because we need to use all the powers from 0 to 4, but the mat_pow() routine is used anyway to show how it works.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* structure to hold all the data for a matrix.  See discussion
 * in comments to understand this structure. */
struct matrix {
    int m_rows, m_cols;
    double **m_data;
    double  *unused_1; /* not used.  Only to warrant the 
                        * alignment to double * values. */
    double   unused_2; /* same as before. */
};

/* this are simple providers of matrix data, depending on how we
 * want to initialize the matrix. */

/* initializes each element to a constant provided by reference
 */
double prov_const(void *cb_par, int row, int col)
{
    double *val_ref = cb_par;
    return *val_ref;
}

double prov_list(void *cb_par, int row, int col)
{
    double **p = cb_par;
    return *(*p)++;
}

double prov_copy(void *cb_par, int row, int col)
{
    struct matrix *src = cb_par;

    assert(row < src->m_rows && col < src->m_cols);
    return src->m_data[row][col];
}

/* initializes kroneker matrix. Identity */
double prov_ident(void *unused, int row, int col)
{
    return row == col ? 1.0 : 0.0;
}

/* provides elements from file stream */
double prov_file(void *cb_par, int row, int col)
{
    FILE *in = cb_par;
    double ret_val;
    if (isatty(fileno(in))) {
        fprintf(stderr,
                "mat[%d][%d]: ",
                row, col);
        fflush(stderr);
    }
    int res = fscanf(in, "%lg", &ret_val);
    assert(res == 1);
    return ret_val;
}

/* this creates the whole matrix with just one malloc */
struct matrix *mat_creat(
    int rows, int cols, /* dimensions */
    double prov(void *cd, int row, int col), /* initializer */
    void *cd) /* initializer callback data */
{
    size_t total_cells = rows * cols;

    /* See discussion about alignment in the comments to
     * understand the code below. */
    struct matrix *ret_val = malloc(
        sizeof *ret_val
        + rows * sizeof *ret_val->m_data
        + total_cells * sizeof **ret_val->m_data);

    assert(ret_val != NULL);

    /* place the array with bigger element cell first,
     * to warrant the smaller data alignment */
    double **aux1; /* pointer to array of row pointers */
    double *aux2; /* pointer to array of double cells */
    if (sizeof *ret_val->m_data > sizeof **ret_val->m_data) {
        /* put the pointers to rows first, pointer is bigger */
        aux1 = (double **) (ret_val + 1); /* row pointers array */
        aux2 = (double *) (aux1 + rows); /* double row arrays */
    } else {
        /* put the cells first, double is bigger than pointer */
        aux2 = (double *) (ret_val + 1);
        aux1 = (double **) (aux2 + total_cells);
    }

    ret_val->m_rows = rows;
    ret_val->m_cols = cols;
    ret_val->m_data = aux1;

    /* start the build */
    for (int row = 0; row < rows; row++) {
        *aux1++ = aux2; /* the pointer to the row */
        if (prov) {
            for (int col = 0; col < cols; col++) {
                /* this provides each element of the row */
                *aux2++ = prov(cd, row, col);
            }
        } else {
            /* no provider, no cell initialization */
            aux2 += cols;
        }
    }
    return ret_val;
}

struct matrix *mat_add(struct matrix *dst, struct matrix *b)
{
    assert(dst->m_rows == b->m_rows
        && dst->m_cols == b->m_cols);

    for (int row = 0; row < dst->m_rows; row++) {
        for (int col = 0; col < dst->m_cols; col++) {
            dst->m_data[row][col] += b->m_data[row][col];
        }
    }
    return dst;
}

struct matrix *mat_mscalar(struct matrix *dst, double b)
{
    for (int row = 0; row < dst->m_rows; row++) {
        for (int col = 0; col < dst->m_cols; col++) {
            dst->m_data[row][col] *= b;
        }
    }
    return dst;
}

struct matrix *mat_prod(struct matrix *a, struct matrix *b)
{
    assert(a->m_cols == b->m_rows);

    struct matrix *ret_val
        = mat_creat(a->m_rows, b->m_cols, NULL, NULL);

    for(int row = 0; row < a->m_rows; row++) {
        for (int col = 0; col < b->m_cols; col++) {
            double accum = 0.0;
            for (int k = 0; k < a->m_cols; k++) {
                accum += a->m_data[row][k]
                       * b->m_data[k][col];
            }
            ret_val->m_data[row][col] = accum;
        }
    }
    return ret_val;
}

trix *mat_pow(struct matrix *a, unsigned N)
{
    /* ensure matrix is square */
    assert(a->m_rows == a->m_cols);

    struct matrix *ret_val
        = mat_creat(a->m_rows, a->m_cols,
                prov_ident, NULL);

    if (N == 0) return ret_val;  /* a**0 -> I */

    /* search for most significant bit */
    unsigned bit = 0;
    while ((1 << bit) < N) bit++;
    bit = 1 << bit;

    while (bit) {
        /* square it */
        struct matrix *aux = mat_prod(ret_val, ret_val);
        free(ret_val); /* must free after multiplying */
        ret_val = aux; /* assign the new matrix. */

        if (bit & N) { /* multiply by a */
            aux = mat_prod(ret_val, a);
            free(ret_val);
            ret_val = aux;
        }
        bit >>= 1;
    }
    return ret_val;
}

ssize_t mat_print(struct matrix *m, FILE *out, char *fmt)
{

#define ACT() do{         \
        if (n < 0) {      \
            return n;     \
        } else {          \
            ret_val += n; \
        }                 \
    } while (0)

    ssize_t ret_val = 0, n;
    char *sep1 = "{";
    for (int row = 0; row < m->m_rows; row++) {
        n = fprintf(out, "%s", sep1);
        ACT();
        sep1 = ",\n ";
        char *sep2 = "{";
        for (int col = 0; col < m->m_cols; col++) {
            n = fprintf(out, "%s", sep2);
            ACT();
            sep2 = ", ";
            n = fprintf(out, fmt, m->m_data[row][col]);
            ACT();
        }
        n = fprintf(out, "}");
        ACT();
    }
    n = fprintf(out, "}\n");
    ACT();
    return ret_val;
}


int main(int argc, char **argv)
{
    static double values[] = {
        1.0, 2.0, 3.0, 4.0,
        0.0, 1.0, 5.0, 6.0,
        0.0, 0.0, 1.0, 7.0,
        0.0, 0.0, 0.0, 1.0,
    };
    static double coefs[] = {
        1.0, -4.0, 6.0, -4.0, 1.0,
    };
    static double zero = 0.0;
    double *p = values;

    struct matrix *M = mat_creat(4, 4, prov_list, &p);
    printf("M =\n");
    mat_print(M, stdout, "%g");

    struct matrix *P = mat_creat(4, 4, prov_const, &zero);

    printf("P[M] = ");
    for (int coef = 0; coef <= 4; coef++) {
        if (coef) {
            if (coefs[coef] > 0.0) {
                printf(" +");
            } else {
                printf(" ");
            }
        }
        printf("%g * %s", coefs[coef], coef ? "A" : "I");
        if (coef > 1) {
            printf("^%d", coef);
        }
    }
    printf(" =\n");
    for (int coef = 0; coef <= 4; coef++) {
        printf("M\n");
        mat_print(M, stdout, "%g");
        printf("**%d\n", coef);
        struct matrix *TN = mat_pow(M, coef);
        mat_print(TN, stdout, "%g");
        TN = mat_mscalar(TN, coefs[coef]);
        printf("*%lg\n", coefs[coef]);
        mat_print(TN, stdout, "%g");
        P = mat_add(P, TN);
        printf("+\n");
        mat_print(P, stdout, "%g");
        free(TN);
    }
    free(P);
    free(M);
}

Would you please try the following:

#include <stdio.h>
#include <stdlib.h>

/*
 * multiply square (MxM) matrices: A = B * C
 */
void multiply(int M, double **A, double **B, double **C)
{
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            A[i][j] = 0;        // initialize cells
            for(int k = 0; k < M; k++) {
                A[i][j] += B[i][k] * C[k][j];
            }
        }
    }
}

/*
 * copy square (MxM) matrix: A := B
 */
void matcpy(int M, double **A, double **B)
{
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            A[i][j] = B[i][j];
        }
    }
}

int main()
{
    int i, j, N, M;
    char buf[BUFSIZ];

    printf("M = ");
    fgets(buf, sizeof buf, stdin);
    sscanf(buf, "%d", &M);

    double **R = malloc(M * sizeof(double *));
    for (i = 0; i < M; i++) {
        R[i] = malloc(M * sizeof(double));
    }

    for (i = 0; i < M; i++) {
        for (j = 0; j < M; j++) {
            printf("R[%d][%d] = ", i, j);
            fgets(buf, sizeof buf, stdin);
            sscanf(buf, "%lf", &R[i][j]);
        }
    }

    printf("N = ");
    fgets(buf, sizeof buf, stdin);
    sscanf(buf, "%d", &N);

    double **S = malloc(M * sizeof(double *));
    for (i = 0; i < M; i++) {
        S[i] = malloc(M * sizeof(double));
    }
    double **C = malloc(M * sizeof(double *));
    for (i = 0; i < M; i++) {
        C[i] = malloc(M * sizeof(double));
    }

    matcpy(M, S, R);
    for (i = 1; i < N; i++) {
        multiply(M, C, R, S);
        matcpy(M, S, C);
    }

    for (i = 0; i < M; i++) {
        for (j = 0; j < M; j++) {
            printf ("%lf ", S[i][j]);
        }
        printf("\n");
    }

    return EXIT_SUCCESS;
}
  • The main reason of the crash is the code arrcpy(M,C,R) where the matrix C is not allocated yet.
  • The code double **R = malloc(M * sizeof(double)) should be double **R = malloc(M * sizeof(double *)) , although the former just happens to work.
  • It is not a good idea to allocate the matrix C each time within the multiply function especially when you perform the successive multiplications.
  • It is recommended to use fgets() and sscanf() instead of scanf() to be robust against irregular inputs.
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
int main()
{int n,P,k;
    printf (" type the number of lines and colons :  ");
scanf ("%d",&n);
    int MUL1[n][n], MUL2[n][n], MUL[n][n];
     int i,j;
     printf ("type the elements of the first matirx : \n");
     for (i=1;i<=n;i++)
    {
        for (j=1;j<=n;j++)
            {printf (" [%d,%d]: ",i,j);
             scanf ("%d",&MUL1[i][j]);}}

     printf ("type the elements of the second matirx :  \n");
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=n;j++)
            {printf (" [%d,%d]: ",i,j);
             scanf ("%d",&MUL2[i][j]);}}

    for (i=1;i<=n;i++)
    {
        for (j=1;j<=n;j++)
            {
                MUL[i][j]=0; 
                for ( k=1;k<=n ; k++)
                {
                       MUL[i][j] = MUL[i][j] + MUL1[i][k] * MUL2[k][j];
                }
            }
    }
    printf ("MAT1 * MAT2 = ");
for (i=1;i<=n;i++)
    {printf ("\n");
        for (j=1;j<=n;j++)
            {printf (" %d ",MUL[i][j]);}}
   return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM