Simpson's method for numerically computing the integration of a function

I have implemented the Simpson's rule for numerical integration.

Check this video for the implemented function.

namespace Simpsons_method_of_integration
{
using System;

public class Simpson
{
private double Function(double x)
{
return 1.0 / (1.0 + Math.Pow(x, 5)); //Define the function f(x)
}

public double Compute(double a, double b, int n)
{
double[] x = new double[n + 1];

double delta_x = (b - a) / n;

x[0] = a;

for (int j = 1; j <= n; j++)//calculate the values of x1, x2, ...xn
{
x[j] = a + delta_x * j;
}

double sum = Function(x[0]);

for (int j = 1; j < n; j++)
{
if (j % 2 != 0)
{
sum += 4 * Function(x[j]);
}
else
{
sum += 2 * Function(x[j]);
}
}

sum += Function(x[n]);

double integration = sum * delta_x / 3;

return integration;
}
}

public class MainClass
{
public static void Main()
{
Simpson simpson = new Simpson();
double a = 0d;//lower limit a
double b = 3d;//upper limit b
int n = 6;//Enter step-length n

if (n % 2 == 0)//n must be even
{
Console.WriteLine(simpson.Compute(a, b, n));
}
else
{
Console.WriteLine("n should be an even number");
}

}
}
}


Output:

1.07491527775614


How can I make this source code more efficient?

This:

    if (j % 2 != 0)
{
sum += 4 * Function(x[j]);
}
else
{
sum += 2 * Function(x[j]);
}


can be expressed as:

    sum += (2 << (j % 2)) * Function(x);


In order to make your algorithm more useful, you should inject the Function as a delegate parameter to the method, and also make it static:

public static double Compute(Func<double, double> fx, double a, double b, int n)
{
double h = (b - a) / n;

double sum = fx(a) + fx(b);
double x = a;
for (int j = 1; j < n; j++)
{
x += h;
sum += (2 << (j % 2)) * fx(x);
}

return sum * h / 3;
}


Used as:

Simpson.Compute(x => 1 / (1 + Math.Pow(x, 5)), a, b, n);

• There is no reason to allocate the double[] x array. It is better to compute the argument as you go: initialize double x = a; and increment it x += delta_x; in the loop body.

• if in the loop is usually a performance killer. Consider incrementing j by 2:

    for (int j = 1; j < n; j += 2)
{
sum += 2 * Function(x);
x += delta_x;
sum += 4 * Function(x);
x += delta_x;
}


Now depending on the parity of n the very last addition could be wrong, and you need to compensate for it.

• Function shall not be a Simpson's method. The Simpson shall not care what it integrates. Change the signature of Compute to accept the callable.

• The choice of $$\\dfrac{1}{1 + x^5}\$$ for an integrand looks strange. It is hard to verify that the result is correct. I recommend to test against some more friendly integrands first.

You don't need the double[].

Example implementation that only loops once and discards the array:

public static double ComputeV2(double a, double b, int n)
{
double deltaX = DeltaX(a, b, n);

double sum = Function(a) + Function(a + deltaX * n);

for (int j = 1; j < n; j++)
{
sum += Function(a + deltaX * j) * (j % 2 == 0 ? 2 : 4);
}

return Integrate(sum, deltaX);
}


And if you're into LINQ, something like this also works:

public static double ComputeLinq(double a, double b, int n)
{
double deltaX = DeltaX(a, b, n);

double sum =
Enumerable
.Range(1, n - 1)
.Select(j => Function(a + deltaX * j) * (j % 2 == 0 ? 2 : 4))
.Aggregate(
seed: Function(a) + Function(a + deltaX * n),
func: (acc, v) => acc + v);

return Integrate(sum, deltaX);
}


As for performance, here are some micro benchmarks:


BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.720 (1909/November2018Update/19H2)
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.301
[Host]     : .NET Core 2.2.6 (CoreCLR 4.6.27817.03, CoreFX 4.6.27818.02), X64 RyuJIT
DefaultJob : .NET Core 2.2.6 (CoreCLR 4.6.27817.03, CoreFX 4.6.27818.02), X64 RyuJIT

|      Method |     Mean |    Error |   StdDev |   Median |
|------------ |---------:|---------:|---------:|---------:|
|   SimpsonV1 | 286.6 ns |  5.53 ns | 11.30 ns | 285.9 ns |
|   SimpsonV2 | 270.1 ns |  5.52 ns | 14.04 ns | 267.0 ns |
| SimpsonLinq | 510.8 ns | 11.67 ns | 34.23 ns | 500.1 ns |


As you can see, using LINQ is clearly slower, but is often more readable (not really in this case, in my opinion).

"My" version is a bit faster than yours, but it's very negligible. The biggest gain is readability and succinctness, but I bet some will find it to be "too" short for its own good.

In regards to performance or efficiency, I don't see anything that begs for attention.

I would suggest Simpson class and its methods be static. You really are not saving any properties or state between invocations, so static makes more sense.

The method named Function is a horrible name. Far too generic.

I'm not even keen on the method name Compute, though it is an action verb. I'd be partial to Integrate which is also an action verb but more descriptive. The parameter names are decent. Since Simpson's Rule uses the non-descript a and b, it's okay that your method does as well. Perhaps n could be given a more descriptive name.

Your use of braces and indentation looks good. Also good that Function is private and Compute is public. However, you do not provide any validation checks on public methods. What if someone entered -999 as the value for n?