Gaussian Elimination

Solving simultaneous equations

Written by Paul Bourke
August 1997


The following note will briefly discuss the standard method of solving simultaneous equations. It will also present C source which implements the so called "partial pivoting" algorithm. An example of 3 simultaneous equations with 3 unknowns x0, x1, x2 is shown below

x0 + x1 + x2 = 0
2x0 + x1 + x2 = 1
x0 + 2x1 + x2 = 15

We wish to determine the values of the 3 unknowns. The solution basically relies on us being able to multiply a row by a constant, and add (or subtract) two rows. So for example the first row above could be multiplied by -2 and then the second row could be added to it to give a new set of equations (but with the same solution)

x0 + x1 + x2 = 0
- x1 - x2 = 1
x0 + 2x1 + x2 = 15

The rows and factor chosen was designed to eliminate the first unknown x0 from the second equation. If we now take -1 times the first row and add the last row we get

x0 + x1 + x2 = 0
- x1 - x2 = 1
+ x1 + 0x2 = 15

And finally, adding the last two rows gives

x0 + x1 + x2 = 0
- x1 - x2 = 1
- x2 = 16

The above is known as the elimination phase, the aim is to turn all the elements below the diagonal into zeros. The solution can now be determined by so called "back substitution". From the last equation we see that x2 is -16. Substituting this value into the second equation gives x1 = 15, and finally substituting these two values into the first equation gives the last unknown x0 = 1.

In general a series of simultaneous equations can be written in matrix notation. If we have n equations with n unknowns (x0, x1, x2, ... xn-1) this can be written as

This is often written in a more compact way as A x = b, where A is a matrix, x and b are vectors. The Gaussian elimination process consists of two steps, first reducing the elements below the diagonal to 0 and second, back substituting to find the solutions. In order to eliminate the appropriate element it is not sufficient to simply use a ratio based automatically on the values on the elements column as this may result in a divide by zero. The usual solution is to swap the rows around so that the element being removed has the largest magnitude.
If it still happens that all elements on the columns to be removed are zero then this reflects a "singular" matrix, that is, the n equations were not all independent and therefore there isn't a unique solution.

The following source code implements the above algorithm in a straightforward way, there are improvements that could be made especially for sparce systems of equations (a large number of equations with mostly zero elements).

C source example
/*
   Solve a system of n equations in n unknowns using Gaussian Elimination
   Solve an equation in matrix form Ax = b
   The 2D array a is the matrix A with an additional column b.
   This is often written (A:b)

   A0,0    A1,0    A2,0    ....  An-1,0     b0
   A0,1    A1,1    A2,1    ....  An-1,1     b1
   A0,2    A1,2    A2,2    ....  An-1,2     b2
   :       :       :             :          :
   :       :       :             :          :
   A0,n-1  A1,n-1  A2,n-1  ....  An-1,n-1   bn-1

   The result is returned in x, otherwise the function returns FALSE
   if the system of equations is singular.
*/
int GSolve(double **a,int n,double *x)
{
   int i,j,k,maxrow;
   double tmp;

   for (i=0;i<n;i++) {

      /* Find the row with the largest first value */
      maxrow = i;
      for (j=i+1;j<n;j++) {
         if (ABS(a[i][j]) > ABS(a[i][maxrow]))
            maxrow = j;
      }

      /* Swap the maxrow and ith row */
      for (k=i;k<n+1;k++) {
         tmp = a[k][i];
         a[k][i] = a[k][maxrow];
         a[k][maxrow] = tmp;
      }

      /* Singular matrix? */
      if (ABS(a[i][i]) < EPS)
         return(FALSE);

      /* Eliminate the ith element of the jth row */
      for (j=i+1;j<n;j++) {
         for (k=n;k>=i;k--) {
            a[k][j] -= a[k][i] * a[i][j] / a[i][i];
         }
      }
   }

   /* Do the back substitution */
   for (j=n-1;j>=0;j--) {
      tmp = 0;
      for (k=j+1;k<n;k++)
         tmp += a[k][j] * x[k];
      x[j] = (a[n][j] - tmp) / a[j][j];
   }

   return(TRUE);
}

A simple calling program which solves the example earlier is

#define ABS(x) (x < 0 ? -(x) : (x))
#define EPS 0.00001
#define TRUE  1
#define FALSE 0

int main(int argc,char **argv)
{
   int i,n = 3;
   double x[3] = {0.0,0.0,0.0};
   double **a;

   a = (double **)malloc((n+1)*sizeof(double *));
   for (i=0;i<n+1;i++)
      a[i] = (double *)malloc(n*sizeof(double));

   a[0][0] = 1;   a[1][0] = 1;   a[2][0] = 1;  a[3][0] = 0;
   a[0][1] = 2;   a[1][1] = 1;   a[2][1] = 1;  a[3][1] = 1;
   a[0][2] = 1;   a[1][2] = 2;   a[2][2] = 1;  a[3][2] = 15;
   GSolve(a,n,x);
   WriteSolution(a,n,x);
}

void WriteSolution(double **a,int n,double *x)
{
   int j,k;

   for (j=0;j<n;j++) {
      for (k=0;k<n+1;k++) {
         printf("%10.3f ",a[k][j]);
      }
      printf(" | %10.3f\n",x[j]);
   }
   printf("\n");
}