Matrix methods in C and C++

Diagonal matrices

Define as matrix M[i,j] = 0 if i != j

For space saving benefits, one uses a single-dimensioned array to store all diagonal elements. Arrays are zero based, whereas matrices covered here are one-based.

The dimensions of the matrix M are given by m and n, and accessed by array A indices i and j.

The diagonal elements at M[i,j] are:

if (i == j)
    A[i-1];

Any diagonal matrix function must check if indices i and j are equal before setting and getting values from the array.

struct Matrix
{
    int A[10];
    int n;      //present dimension of the matrix (the dimension of A[] must be at least this)
};

void SetDiagonal(struct Matrix *m, int i, int j, int x)
{
    if(i == j)
        m->A[i-1] = x;
}

int GetDiagonal(struct Matrix m,int i,int j)
{
    if(i == j)
    return m.A[i-1];
    else
    return 0;
}

//general method for all matrices
void DisplayMatrix(struct Matrix m)
{
    int i,j;
    for(i = 0; i < m.n; i++)
    {
        for(j = 0; j < m.n; j++)
        {
            if(i == j)
                printf("%d ", m.A[i]);
            else
                printf("0 ");
        }
        printf("\n");
    }
}

Class implementation of diagonal matrices

Moving towards a C++ class form of a matrix, as opposed to C structure:

class Diagonal
{
    private:
        int *A;
        int n;

    public:
        Diagonal()
        {
            n = 2;
            A = new int[2];
        }

        Diagonal(int n)
        {
            this->n = n;
            A = new int[n];
        }

        ~Diagonal()
        {
            delete []A;
        }

        void Set(int i, int j, int x);
        int Get(int i, int j);
        void Display();
        int GetDimension(){return n;}
};

void Diagonal::Set(int i, int j, int x)
{
    if(i == j)
    A[i-1] = x;
}

int Diagonal::Get(int i, int j)
{
    if(i == j)
    return A[i-1];
    return 0;
}

void Diagonal::Display()
{
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            if(i == j)
                cout << A[i-1] << " ";
            else
                cout << "0 ";
        }
    cout << endl;
    }
}

Lower-triangular matrices

This can defined as an array A[i,j] such that

if (i < j)
    A[i-1] = 0;

The matrix is stored in a single-dimensioned array. The number of elements in the lower triangle is the ceiling value of:

1 + 2 + 3 + ... + n = n(n+1)/2 elements

Given the dimensions of the square matrix, one can then initialise a single-dimensioned array of length n(n+1)/2. There are two methods of filling the array: row-by-row (or row major mapping/representation) or column-by-column (or column major mapping/representation).

Row-major mapping

Access to row 3, column 2 is achieved by skipping to row 3, and then adding two elements. Generally, for i > 0 and j > 0, M[i][j] = A[(1 + ... + i-1) + j-1] which is M[i][j] = A[(i(i-1)/2) + j-1].

Column-major mapping

This approach focuses on filling by column instead of row. With matrix M[i][j]:

  • the index of M[3][3] is [4 + 3] + 0

  • the index of M[4][3] is [4 + 3] + 1

  • the index of M[4][4] is [4 + 3 + 2] + 0

The first part [] can br related to j as [n + (n-1) + (n-2) + ... + (n-(j-2))]. The second part is the difference i-j. The general forumla is the sum of these two parts, that is, the index in A[] of M[i][j] is

  • [n + (n-1) + ... + (n-(j-2))] + i - j

Note there are j-1 terms in the first part []. Thus, the general formula simplifies to:

  • M[i][j] = [n(j-1) - (1 + 2 + ... + (j-2))] + i - j

Finally, we combine more terms in [], to give:

  • M[i][j] = [n(j-1) - ((j-2)(j-1)/2)] + i - j

The C based structure approach for column-major mapping is given below:

struct Matrix
{
    int *A;
    int n;
};

void SetLowerDiag(struct Matrix *m, int i, int j, int x)
{
    if(i >= j)
        m->A[m->n*(j-1) + (j-2)*(j-1)/2 + i-j] = x;
}

int GetLowerDiag(struct Matrix m, int i, int j)
{
    if(i >= j)
        return m.A[m.n*(j-1) + (j-2)*(j-1)/2 + i-j];
    else
        return 0;
}

void DisplayLowerDiag(struct Matrix m)
{
    int i, j;
    for(i = 1; i <= m.n; i++)    
        {
            for(j = 1; j <= m.n; j++)        
            {
                if(i >= j)
                    printf("%d ",m.A[m.n*(j-1) + (j-2)*(j-1)/2 + i-j]);
                else
                    printf("0 ");
            }
        printf("\n");
        }
}

Upper-triangular matrices

The algorithms which handle upper-triangular matrices are similar to that of lower-triangular matrices but with the difference of

a. swapping row-major for column-major

b. the swapping the index i with index j

c. changing the requirement so we set or get values only if i<=j

We perform (a) because the array of a row-major mapped upper-triangular matrix is the same form as that of the column-major mapped lower-triangular matrix. The first set of elements in the array is composed of n elements from the nxn matrix M.

The code for finding the index in an array which follows row-major of an upper-triangular matrix is:

M[i][j] = [n(i-1) - ((i-2)(i-1)/2)] + j - i

The above formula was previously used as the column-major representation of a lower-triangular matrix:

M[i][j] = [n(j-1) - ((j-2)(j-1)/2)] + i - j

Symmetric matrices

Essentially, M[i][j] = M[j][i]. One can store the elements from the ‘lower-triangle’ or ‘upper-triangle’ part of the matrix and store it in a single-dimensioned array. The remaining elements in the matrix are populated automatically (this follows directly from the column-major mapping of C based structure, Matrix):

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

struct Matrix
{
    int *A;
    int n;
};

void Set(struct Matrix *m, int i, int j, int x)
{
    if(i >= j)
        m->A[m->n*(j-1) + (j-2)*(j-1)/2 + i-j] = x;
}

int Get(struct Matrix m, int i, int j)
{
    if(i >= j)
        return m.A[m.n*(j-1) + (j-2)*(j-1)/2 + i-j];
    else
        return 0;
}

void DisplaySymmetric(struct Matrix m)
{
    int i,j;

    for(i = 1; i <= m.n; i++)
    {
        for(j = 1; j <= m.n; j++)
        {
            if(i >= j)
                //print the lower triangle as before
                printf("%d ", m.A[m.n*(j-1) + (j-2)*(j-1)/2 + i-j]);
            else
                //unlike displaying lower triangular matrices, invert indices i and j and print
                printf("%d ", m.A[m.n*(i-1) + (i-2)*(i-1)/2 + j-i]);
        }
        printf("\n");
    }
}

int main()
{
    struct Matrix m;
    int i, j, x;
    printf("Enter Dimension\n");
    scanf("%d", &m.n);
    m.A = (int *) malloc(m.n*(m.n+1)/2 * sizeof(int));
    printf("enter all elements\n");

    for(i = 1; i <= m.n; i++)
    {
        for(j = 1; j <= m.n; j++)
        {
            scanf("%d", &x);
            Set(&m, i, j, x);
        }
    }
    printf("\n\n");
    DisplaySymmetric(m);
    return 0;
}

The program is located in /code/SymmetricMatrices/.

Tridiagonal matrices

Defined such that all elements off the diagonal (i-j = 0), the upper (i-j = -1) and lower (i-j = 1) diagonals are zero. Overall, all values of the matrix M[i][j] = 0 for ||i-j|| > 1.

There will be 3n - 2 elements on the diagonals, for any nxn square matrix. We store them in a single-dimensioned array diagonal by diagonal.

Once the requirements in terms of i and j are met, one can access the elements in array A[] from their matrix M indices using:

  • lower diagonal (i-j = 1): index(A[i][j]) = i - 1

  • diagonal (i-j = 0): index(A[i][j]) = n - 1 + (i-1)

  • upper diagonal (i-j = -1): index(A[i][j]) = 2n - 1 + (i-1)

Approach the method by first asking for the indices of the element in matrix M required. Decide which diagonal the user has asked for using if (i-j == -1){...} else . Then apply the above expression to retrieve the index of the element in array A[]. Then display or update the value.

A tridiagonal matrix is one type of square-band matrix where the number of diagonals on either side of the diagonal is equal but can be greater than one. All elements off the diagonals are zero.

Toeplitz matrices

This is matrix in which all elements in the same diagonal are equal. That is, M[i][j] = M[i-1][j-1].

There is not requirement for the values between each diagonal to equal or not. The storage of such a matrix can be achieved by again using a single-dimensioned array and recording the values of the first row and the first column. This would require n + (n-1) elements.

The user can could input the first row and column values. The display of the Toeplitz matrix can be achieved with the following:

  • upper triangle (i <= j): index(A[i][j]) = j - i

  • lower triangle (i > j): index(A[i][j]) = n + i - j - 1

The approach is similar. Ask for the indices of the element in matrix M required. Decide which diagonal the user has asked for using if (i <= j){...} else . Then apply the above expression to retrieve the index of the element in array A[]. Then display or update the value.