If you want to save space and the overhead of allocating each row of the matrix, you can implement a triangular matrix using smart indexing of one array.
The lower triangular matrix (including diagonals) has the following properties:
Dimension Matrix Elements / row Total elements
1 x . . eleven
2 xx. . 2 3
3 xxx. 3 6
4 xxxx 4 10
...
Total number of elements for a given dimension:
size(d) = 1 + 2 + 3 + ... + d = (d+1)(d/2)
If you put rows sequentially in one array, you can use the above formula to calculate the offset of a given row and column (both based on zero) inside the matrix:
index(r,c) = size(r-1) + c
The above formulas for the lower triangular matrix. You can access the upper matrix as if it were the lower matrix by simply changing the indices:
index((d-1)-r, (d-1)-c)
If you have problems changing the orientation of the array, you can develop a different offset calculation for the upper array, for example:
uindex(r,c) = size(d)-size(dr) + cr
Code example:
#include <time.h> #include <stdio.h> #include <stdlib.h> #define TRM_SIZE(dim) (((dim)*(dim+1))/2) #define TRM_OFFSET(r,c) (TRM_SIZE((r)-1)+(c)) #define TRM_INDEX(m,r,c) ((r)<(c) ? 0 : (m)[TRM_OFFSET((r),(c))]) #define TRM_UINDEX(m,r,c,d) ((r)>(c)?0:(m)[TRM_SIZE(d)-TRM_SIZE((d)-(r))+(c)-(r)]) #define UMACRO 0 int main (void) { int i, j, k, dimension; int *ml, *mu, *mr; printf ("Enter dimension: "); if (!scanf ("%2d", &dimension)) { return 1; } ml = calloc (TRM_SIZE(dimension), sizeof *ml); mu = calloc (TRM_SIZE(dimension), sizeof *mu); mr = calloc (dimension*dimension, sizeof *mr); if (!ml || !mu || !mr) { free (ml); free (mu); free (mr); return 2; } /* Initialization */ srand (time (0)); for (i = 0; i < TRM_SIZE(dimension); i++) { ml[i] = 100.0*rand() / RAND_MAX; mu[i] = 100.0*rand() / RAND_MAX; } /* Multiplication */ for (i = 0; i < dimension; i++) { for (j = 0; j < dimension; j++) { for (k = 0; k < dimension; k++) { mr[i*dimension + j] += #if UMACRO TRM_INDEX(ml, i, k) * TRM_UINDEX(mu, k, j, dimension); #else TRM_INDEX(ml, i, k) * TRM_INDEX(mu, dimension-1-k, dimension-1-j); #endif } } } /* Output */ puts ("Lower array"); for (i = 0; i < dimension; i++) { for (j = 0; j < dimension; j++) { printf (" %2d", TRM_INDEX(ml, i, j)); } putchar ('\n'); } puts ("Upper array"); for (i = 0; i < dimension; i++) { for (j = 0; j < dimension; j++) { #if UMACRO printf (" %2d", TRM_UINDEX(mu, i, j, dimension)); #else printf (" %2d", TRM_INDEX(mu, dimension-1-i, dimension-1-j)); #endif } putchar ('\n'); } puts ("Result"); for (i = 0; i < dimension; i++) { for (j = 0; j < dimension; j++) { printf (" %5d", mr[i*dimension + j]); } putchar ('\n'); } free (mu); free (ml); free (mr); return 0; }
Please note that this is a trivial example. You can expand it to wrap the pointer over the matrix inside the structure, which also stores the type of matrix (upper or lower triangle or square), as well as the dimensions and write access functions that work accordingly, depending on the type of matrix.
For any non-trivial use of matrices, you should probably use a third-party library that specializes in matrices.