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();
}