Better implementation of Gaussian Elimination

Posted on

Problem

I made an algorithm in C# that solves any system of linear equations using the Gaussian elimination. There are 2 text boxes in the program for input and output. Input is in the format of the coefficients of the variables separated by spaces and lines. I want to know if this code can be cut shorter or optimized somehow.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Equations_Solver
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox2.Clear();
            double[][] rows = new double[textBox1.Lines.Length][];
            for (int i = 0; i < rows.Length; i++)
            {
                rows[i] = (double[])Array.ConvertAll(textBox1.Lines[i].Split(' '), double.Parse);
            }

            int length = rows[0].Length;
            for (int i = 0; i < rows.Length - 1; i++)
            {
                for (int j = i; j < rows.Length; j++)
                {
                    double[] d = new double[length];
                    for (int x = 0; x < length; x++)
                    {
                        if (i == j && rows[j][i] == 0)
                        {
                            bool changed = false;
                            for (int z = rows.Length - 1; z > i; z--)
                            {
                                if (rows[z][i] != 0)
                                {
                                    double[] temp = new double[length];
                                    temp = rows[z];
                                    rows[z] = rows[j];
                                    rows[j] = temp;
                                    changed = true;
                                }
                            }
                            if (!changed)
                            {
                                textBox2.Text += "No Solutionrn";
                                return;
                            }
                        }
                        if (rows[j][i] != 0)
                        {
                            d[x] = rows[j][x] / rows[j][i];
                        }
                        else
                        {
                            d[x] = rows[j][x];
                        }
                    }
                    rows[j] = d;
                }
                for (int y = i + 1; y < rows.Length; y++)
                {
                    double[] f = new double[length];
                    for (int g = 0; g < length; g++)
                    {
                        if (rows[y][i] != 0)
                        {
                            f[g] = rows[y][g] - rows[i][g];
                        }
                        else
                        {
                            f[g] = rows[y][g];
                        }
                    }
                    rows[y] = f;
                }
            }
            double val = 0;
            int k = length - 2;
            double[] result = new double[rows.Length];
            for (int i = rows.Length - 1; i >= 0; i--)
            {
                val = rows[i][length - 1];
                for (int x = length - 2; x > k; x--)
                {
                    val -= rows[i][x] * result[x];
                }
                result[i] = val / rows[i][i];
                if (result[i].ToString() == "NaN" || result[i].ToString().Contains("Infinity"))
                {
                    textBox2.Text += "No Solution Found!n";
                    return;
                }
                k--;
            }
            for (int i = 0; i < result.Length; i++)
            {
                textBox2.Text += string.Format("X{0} = {1}rn", i + 1, Math.Round(result[i], 10));
            }
        }
    }
}

A sample input for solving a system of equations like X + 2Y = 3 and 4X + 5Y = 6 would be like this:

1 2 3
4 5 6

and the output would be:

X1 = -1
X2 = 2

Solution

If you concatenate strings inside a loop it is much better to use a stringbuilder.


Instead of placing the whole code in a button click handler and accessing the textboxes, you should give this code an own method which is then called in the handler.


This if condition

for (int x = 0; x < length; x++)
{
    if (i == j && rows[j][i] == 0)
    {  

can only be true for the first iteration so if you extract the swapping of the array elements to a separate method which is called before this loop, you won’t need to check this for every iteration.


if (rows[j][i] != 0)
{
    d[x] = rows[j][x] / rows[j][i];
}
else
{
    d[x] = rows[j][x];
}  

By just setting d[x] = rows[j][x] before the if statement you could reduce this to

d[x] = rows[j][x];
if (rows[j][i] != 0)
{
    d[x] =  d[x] / rows[j][i];
}

The same should be done for

if (rows[y][i] != 0)
{
    f[g] = rows[y][g] - rows[i][g];
}
else
{
    f[g] = rows[y][g];
}  

This

double val = 0;
int k = length - 2;
double[] result = new double[rows.Length];
for (int i = rows.Length - 1; i >= 0; i--)
{
    val = rows[i][length - 1];
    for (int x = length - 2; x > k; x--)
    {
        val -= rows[i][x] * result[x];
    }
    result[i] = val / rows[i][i];
    if (result[i].ToString() == "NaN" || result[i].ToString().Contains("Infinity"))
    {
        textBox2.Text += "No Solution Found!n";
        return;
    }
    k--;
}  

should be extracted to its own method and should be simplified by removing k and change the inner loop to

for (int x = length - 2; x > i - 1; x--)  

By extracting the splitting and converting of the string[] to a separate method, you gain in readability and separation of concerns.


Applying the mention points will lead to

private double[] SolveLinearEquations(string[] input)
{
    double[][] rows = new double[input.Length][];
    for (int i = 0; i < rows.Length; i++)
    {
        rows[i] = (double[])Array.ConvertAll(input[i].Split(' '), double.Parse);
    }
    return SolveLinearEquations(rows);
}

private double[] SolveLinearEquations(double[][] rows)
{

    int length = rows[0].Length;

    for (int i = 0; i < rows.Length - 1; i++)
    {
        if (rows[i][i] == 0 && !Swap(rows, i, i))
        {
            return null;
        }

        for (int j = i; j < rows.Length; j++)
        {
            double[] d = new double[length];
            for (int x = 0; x < length; x++)
            {
                d[x] = rows[j][x];
                if (rows[j][i] != 0)
                {
                    d[x] = d[x] / rows[j][i];
                }
            }
            rows[j] = d;
        }

        for (int y = i + 1; y < rows.Length; y++)
        {
            double[] f = new double[length];
            for (int g = 0; g < length; g++)
            {
                f[g] = rows[y][g];
                if (rows[y][i] != 0)
                {
                    f[g] = f[g] - rows[i][g];
                }

            }
            rows[y] = f;
        }
    }

    return CalculateResult(rows);
}

private bool Swap(double[][] rows, int row, int column)
{
    bool swapped = false;
    for (int z = rows.Length - 1; z > row; z--)
    {
        if (rows[z][row] != 0)
        {
            double[] temp = new double[rows[0].Length];
            temp = rows[z];
            rows[z] = rows[column];
            rows[column] = temp;
            swapped = true;
        }
    }

    return swapped;
}
private double[] CalculateResult(double[][] rows)
{
    double val = 0;
    int length = rows[0].Length;
    double[] result = new double[rows.Length];
    for (int i = rows.Length - 1; i >= 0; i--)
    {
        val = rows[i][length - 1];
        for (int x = length - 2; x > i - 1; x--)
        {
            val -= rows[i][x] * result[x];
        }
        result[i] = val / rows[i][i];

        if (!IsValidResult(result[i]))
        {
            return null;
        }
    }
    return result;
}

private bool IsValidResult(double result)
{
    return !(double.IsNaN(result) || double.IsInfinity(result));
} 

which can then be called like

double[] result = SolveLinearEquations(textBox1.Lines);  

textBox2.Clear();

textBox2.Text = ConvertToString(result);  

where ConvertToString() will look like

private string ConvertToString(double[] result)
{
    StringBuilder sb = new StringBuilder(1024);
    for (int i = 0; i < result.Length; i++)
    {
        sb.AppendFormat("X{0} = {1}rn", i + 1, Math.Round(result[i], 10));
    }
    return sb.ToString();
}

Leave a Reply

Your email address will not be published. Required fields are marked *