In this article, I describe Gauss’ algorithm for solving n linear equations with n unknowns. I also give a sample implementation in C.

The Problem

Let’s say you want to solve the following system of 3 equations with 3 unknowns:
eqset.png

Humans learn that there a two ways to solve this system. Reduction and substitution. Unfortunately, neither of these methods is suitable for a computer.

A simple algorithm (and the one used everywhere even today), was discovered by Gauss more than two hundred years ago. Since then, some refinements have been found, but the basic procedure remains unchanged.

Gaussian Elimination

Start by writing the system in matrix form:
s1.png

If you recall how matrix multiplication works, you’ll see that’s true. If not, it’s enough to notice how the matrix is written: the coefficients of x, y and z are written, side by side, as the rows of a 3×3 matrix; x, y and z are then written as rows of a 3×1 matrix; finally, what’s left of the equality sign is written one under the other as a 3×1 matrix.

So far, this doesn’t actually help, but it does make the following process easier to write. The goal is, through simple transformations, to reach the system, where a, b and c are known.
s2.png

How do you transform the initial system into the above one? Here’s Gauss’ idea.

Start with the initial system, then perform some operations to get 0s on the first column, on every row but the first.
s3.png

The operations mentioned are multiplying the first rows by -3/2 and substracting it from the second. Then multiplying the first rows by -1 and substracting it from the third. What is -3/2? It’s first element of the second row divided by the first element of the first row. And -1? It’s the first element of the third row divided by the first element of the first row. NOTE The changes to row 1 are never actually written back into the matrix.

For now we’re done with row 1, so we move on to row 2. The goal here is to get every row on the second column under row 2 to 0. We do this by multiplying the second rows by 4 (i.e. 2 / (1 / 2)) and substracting it from the third rows.
s4.png

Now it’s easy to find the value of z. Just multiply the third column by -1 (i.e. -1/1).
s5.png

ERRATA: The 7 in the above matrix should be an 8.

Knowing the value of z, we can now eliminate it from the other two equations.
s6.png

Now, we can find the value of y and eliminate y from the first equation.
s7.png

s8.png

And, finally, the value of x is:
s9.png

And with that, we’re done.

The Programme

Unfortunately, this is easier said than done. The actual computer programme has to take into account divisions by zero and numerical instabilities. This adds to the complexity of forwardSubstitution().

Here’s the code in C (gauss.c):


#include <stdio.h>

int n;
float a[10][11];

void forwardSubstitution() {
        int i, j, k, max;
        float t;
        for (i = 0; i < n; ++i) {
                max = i;
                for (j = i + 1; j < n; ++j)
                        if (a[j][i] > a[max][i])
                                max = j;

                for (j = 0; j < n + 1; ++j) {
                        t = a[max][j];
                        a[max][j] = a[i][j];
                        a[i][j] = t;
                }

                for (j = n; j >= i; –j)
                        for (k = i + 1; k < n; ++k)
                                a[k][j] -= a[k][i]/a[i][i] * a[i][j];

/*              for (k = 0; k < n; ++k) {
                        for (j = 0; j < n + 1; ++j)
                                printf("%.2f\t", a[k][j]);
                        printf("\n");
                }*/
        }
}

void reverseElimination() {
        int i, j;
        for (i = n - 1; i >= 0; –i) {
                a[i][n] = a[i][n] / a[i][i];
                a[i][i] = 1;
                for (j = i - 1; j >= 0; –j) {
                        a[j][n] -= a[j][i] * a[i][n];
                        a[j][i] = 0;
                }
        }
}

void gauss() {
        int i, j;

        forwardSubstitution();
        reverseElimination();

        for (i = 0; i < n; ++i) {
                for (j = 0; j < n + 1; ++j)
                        printf("%.2f\t", a[i][j]);
                printf("\n");
        }
}

int main(int argc, char *argv[]) {
        int i, j;

        FILE *fin = fopen("gauss.in", "r");
        fscanf(fin, "%d", &n);
        for (i = 0; i < n; ++i)
                for (j = 0; j < n + 1; ++j)
                        fscanf(fin, "%f", &a[i][j]);
        fclose(fin);

        gauss();

        return 0;
}

In the above code, the first two for-s of forwardSubstitution(), just swap two rows so as to diminish the possibilities of some bad rounding errors. Also, if it exists with a division by zero error, that probably means the system cannot be solved.

And here’s the input file for the example (gauss.in) (save it as gauss.in):
3
2 1 -1 8
-3 -1 2 -11
-2 1 2 -3

That’s it. Good luck. Always open to comments.