Simpson's Rule is based on approximating the curve with a parabola (i.e., a quadratic polynomial), instead of a straight line.
Mathematical Proof
Simpson's Rule approximates the integrand using a quadratic polynomial. Suppose we have three points on the interval ([a, b]): (a), (m) (the midpoint), and (b). We approximate the function values at these three points with a quadratic polynomial (P(x)):
Through interpolation, we can ensure that (P(a) = f(a)), (P(m) = f(m)), and (P(b) = f(b)). This quadratic polynomial captures the function's changes over the interval better than a straight line.
Interpolation Calculation
First, we write three equations:
Starting from the first equation:
Setting (x = a), we get:
Similarly, setting (x = b), we have:
Setting (x = \frac{a+b}{2}), we obtain:
Now, we have three equations:
Eliminating (C) from equations (1) and (2):
Subtracting (5) from (4), we have:
Since (a^2 - b^2 = (a+b)(a-b)):
Factoring out (a-b):
Thus:
Substituting (6) into (3):
Simplifying:
Thus, (A), (B), and (C) can be expressed using (a), (b), (C), and (f(x)) as follows:
Thus, we can write out the specific expression for the interpolation function (P(x)):
Substituting (A), (B), and (C) completely into the equation (Ax^2 + Bx + C), we get:
Integration
We integrate the expression we just obtained.
Here we calculate the definite integral of the function from (a) to (b).
from sympy import symbols, Function, integrate, simplify
# Define symbols
x, a, b, c = symbols('x a b c')
f = Function('f')
# Define coefficients A, B, C
A = 2*f(a)/(a**2 - 2*a*b + b**2) + 2*f(b)/(a**2 - 2*a*b + b**2) - 4*f(a/2 + b/2)/(a**2 - 2*a*b + b**2)
B = -a*f(a)/(a**2 - 2*a*b + b**2) - 3*a*f(b)/(a**2 - 2*a*b + b**2) + 4*a*f(a/2 + b/2)/(a**2 - 2*a*b + b**2) - 3*b*f(a)/(a**2 - 2*a*b + b**2) - b*f(b)/(a**2 - 2*a*b + b**2) + 4*b*f(a/2 + b/2)/(a**2 - 2*a*b + b**2)
C = a**2*f(b)/(a**2 - 2*a*b + b**2) + a*b*f(a)/(a**2 - 2*a*b + b**2) + a*b*f(b)/(a**2 - 2*a*b + b**2) - 4*a*b*f(a/2 + b/2)/(a**2 - 2*a*b + b**2) + b**2*f(a)/(a**2 - 2*a*b + b**2)
# Define the equation Ax^2 + Bx + C
expr = A*x**2 + B*x + C
# Calculate the definite integral
integral_result = integrate(expr, (x, a, b))
simp = simplify(integral_result)
# Print the result
print("Definite integral result:")
print(simp)
The output is as follows:
-a*f(a)/6 - a*f(b)/6 - 2*a*f(a/2 + b/2)/3 + b*f(a)/6 + b*f(b)/6 + 2*b*f(a/2 + b/2)/3
Which simplifies to:
Continuing to simplify:
We define (h = \frac{b-a}{2}), (m = \frac{a+b}{2})
We already know:
So:
Can be simplified to:
Now, the remaining part is:
This can be rearranged as:
Substituting (a = m - h) and (b = m + h):
Combining these two parts:
This is the expression we need:
This is the final expression for Simpson's Rule.
By taking more points (a, b), we can accurately approximate any given function with a quadratic function, thereby using numerical methods to calculate the definite integral of any function.
Code Verification
In the code, we calculate separately and calculate and separately. Since all and values, except for the first and last, act as the end of one interval and the start of another, they need to be multiplied by 2.
To run the code properly, we need to install the following dependencies: libomp for parallel computing and clang++ for compiling.
#include <iostream>
#include <cmath>
#include <stdexcept>
#include <functional>
#include <omp.h>
// Define the function f(x)
long double f(long double x) {
return std::tan(x) + 114514 * std::pow(x, 1.0235) + 0.6 * std::sin(1 / std::pow(2 * x + 1, std::tan(std::pow(x, 0.14))));
}
// Use Simpson's Rule to approximate the definite integral
long double simpsons_rule(std::function<long double(long double)> f, long double a, long double b, long long n) {
if (n % 2 != 0) {
throw std::invalid_argument("n must be an even integer.");
}
long double h = (b - a) / n;
long double total = f(a) + f(b);
long double sum1 = 0.0;
long double sum2 = 0.0;
#pragma omp parallel for reduction(+:sum1)
for (long long i = 1; i < n; i += 2) {
sum1 += 4 * f(a + i * h);
}
#pragma omp parallel for reduction(+:sum2)
for (long long i = 2; i < n; i += 2) {
sum2 += 2 * f(a + i * h);
}
total += sum1 + sum2;
return total * (h / 3);
}
int main() {
try {
long double a = 0.1; // Lower limit of integration
long double b = 1; // Upper limit of integration
long long n = 10000000000LL; // Number of subintervals (must be an even number)
long double simpsons_result = simpsons_rule(f, a, b, n);
std::cout << "Simpson's Rule Result: " << simpsons_result << std::endl;
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
We can compile and run with the following command:
clang++ -Ofast -march=native -Xpreprocessor -fopenmp -I$(brew --prefix libomp)/include -L$(brew --prefix libomp)/lib -lomp -mfpu=neon -mfloat-abi=hard -o integrate ./*.cpp -lm -std=c++20 -funroll-loops && ./integrate