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 Solution\r\n";
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}\r\n", 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
1 Answer 1
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}\r\n", i + 1, Math.Round(result[i], 10));
}
return sb.ToString();
}
-
\$\begingroup\$ I think this was a great answer, I just have a couple suggestions to add to it: d[x] = rows[j][x]; if (rows[j][i] != 0) { d[x] = d[x] / rows[j][i]; } could be shortened to: d[x] = (rows[j][i] != 0) ? d[x] / rows[j][x] : rows[j][x]; but this could be less readable to some. My other suggestion would be to take out the two for loops and make them separate functions since the SolveLinearEquations function seems a bit long to me (but this is a personal preference). Other than that, great answer! \$\endgroup\$Steven Kohus– Steven Kohus2015年02月04日 21:54:53 +00:00Commented Feb 4, 2015 at 21:54
-
\$\begingroup\$ @StevenKohus I have thought about extracting these loops to separate methods when I wrote this answer, but I couldn't come up with a good and meaningful methodname. \$\endgroup\$Heslacher– Heslacher2015年02月05日 05:45:20 +00:00Commented Feb 5, 2015 at 5:45
-
1\$\begingroup\$ Instead of using
result.ToString() != "NaN" || !result.ToString().Contains("Infinity")
, one should probably usedouble.IsNaN(result)
anddouble.IsInfinity(result)
. \$\endgroup\$Christoph– Christoph2019年05月21日 07:00:18 +00:00Commented May 21, 2019 at 7:00 -
\$\begingroup\$ @Christoph good catch. In addition the condition had been wrong as well. \$\endgroup\$Heslacher– Heslacher2019年05月21日 07:36:06 +00:00Commented May 21, 2019 at 7:36
Explore related questions
See similar questions with these tags.