I have a simple bisection method solver that I was told it has some problems in design and I need my code to be reviewed, so I was hoping for someone to give me guideline on how to improve my code.
It starts with a delegate:
public delegate double MyFun (double x) ;
A class to add functions:
class functions
{
public static double fun1(double x) => Math.Pow(x,2) - 3;
public static double fun2(double x) => 5* Math.Pow(x, 3) - 2* Math.Pow(x, 2) + 3 * x - 2;
public static double fun3(double x) => 2 * Math.Sqrt(x) ;
}
A class that has a function that solves the equation. It has a function that takes 4 input parameters: delegate, start point, end point, guess of solution.
class Bisection
{
public static double Bisection_method (MyFun fun , double start , double end , double? guess)
{
if ( fun(start) * fun(end) > 0 )
{
Console.WriteLine("wrong Entry");
return -1;
}
if (fun(start) == 0)
{
return start;
}
double avg,tolerance,sign;
avg = (guess.HasValue) ? guess.Value : ( (start + end) / 2 );
do
{
tolerance = Math.Abs ( fun(end) - fun(start) );
sign = fun(start) * fun(avg);
if (sign < 0)
end = avg;
else if (sign > 0)
start = avg;
avg = (start + end) / 2;
}
while ( tolerance > 0.0001 );
return end;
}
}
Then the main method:
class Program
{
static void Main(string[] args)
{
MyFun fun1 = functions.fun1;
double start = 0;
double end = 500;
double guess = 10;
double answer = Bisection.Bisection_method(fun1, start, end, guess);
Console.WriteLine($"The Root = {answer}");
}
}
I was hoping for someone to help me how to improve this simple code design and there is also some cases that I need to handle.
2 Answers 2
There is a of lot room for improvements in your code so let us start straight ahead with your class
functions
Based on the .NET Naming Guidelines classes should be named using
PascalCase
casing which isn't the only problem here. Naming things is hard but its much harder to grasp at first glance what a class, method or field is used for if one uses names likefunction
,MyFun
orfun1
..fun3
. If you come back in a few weeks/months to fix a bug or to add a feature you still need to understand quickly what the code is doing which will be harder if you keep these names.Math.Pow(x,2)
should always be replaced byx * x
which will be faster
Bisection
You are executing
fun(start)
at least one time but up to four times. By storing the result of the method call inside a variable you code will be easier to read and also be faster. The same applies tofun(end)
.double avg,tolerance,sign;
don't do this. Its to hard to read. Always declare one variable per line.omitting braces
{}
is valid in C# but it is dangerous as well because it can lead to hidden and therefor hard to find bugs. But if you decide for yourself to take the risk you should at least be consequent with your style. Right now you are sometimes using braces and sometimes you don't.If a value of a method parameter doesn't fit in the range of an expected value one should throw an
ArgumentOutOfRangeException
or if the value is plainly wrong one should throw anArgumentException
instead of writing to the console and returning a magic number (-1
).Numbers used like
tolerance > 0.0001
should be stored in a meaningful named constant so every reader of the code exactly knows what0.0001
represents.
Heslacher's review covers most of what I would say, but there are a couple of things to add about the main loop:
do { tolerance = Math.Abs ( fun(end) - fun(start) ); sign = fun(start) * fun(avg); if (sign < 0) end = avg; else if (sign > 0) start = avg; avg = (start + end) / 2; } while ( tolerance > 0.0001 );
tolerance
makes more sense to me as the name of the constant0.0001
than as the width of the interval under consideration.- Why calculate the width of the interval at the start of the loop, before updating
start
/end
? There may be valid reasons, but if so they should be commented because it's a surprising place to calculate it. - I think there's a bug. If
avg
is exactly the root,sign
will be zero, so neitherend
norstart
will change, locking the execution in an infinite loop.
-
\$\begingroup\$ What OP calls
tolerance
should be renamedrange
. Along with adding aTolerance
constant the final loop condition would becomewhile (range > Tolerance);
. \$\endgroup\$Rick Davin– Rick Davin2017年10月10日 13:19:49 +00:00Commented Oct 10, 2017 at 13:19
fun3
do? Pretty hard to answer that... \$\endgroup\$