5
\$\begingroup\$

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
svick
24.5k4 gold badges53 silver badges89 bronze badges
asked Feb 3, 2015 at 13:31
\$\endgroup\$
0

1 Answer 1

6
\$\begingroup\$

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();
}
answered Feb 3, 2015 at 14:47
\$\endgroup\$
4
  • \$\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\$ Commented 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\$ Commented Feb 5, 2015 at 5:45
  • 1
    \$\begingroup\$ Instead of using result.ToString() != "NaN" || !result.ToString().Contains("Infinity"), one should probably use double.IsNaN(result) and double.IsInfinity(result). \$\endgroup\$ Commented May 21, 2019 at 7:00
  • \$\begingroup\$ @Christoph good catch. In addition the condition had been wrong as well. \$\endgroup\$ Commented May 21, 2019 at 7:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.