Simpson’s Rule for Approximating Definite Integrals in R

[This article was first published on R – Aaron Schlegel, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Part 9 of 9 in the series Numerical Analysis

Simpson’s rule is another closed Newton-Cotes formula for approximating integrals over an interval with equally spaced nodes. Unlike the trapezoidal rule, which employs straight lines to approximate a definite integral, Simpson’s rule uses the third Lagrange polynomial, P3(x) to approximate the definite integral and as such can give exact results when approximating integrals of up to third-degree polynomials. Simpson’s rule is also generally more performant and often converges faster compared to the trapezoid rule.

Simpson’s rule is defined as:

x2x0f(x) dx=h3[f(x0)+4f(x1)+f(x2)]h290f(4)(ϵ)

Where x1=x0+h, h=(x2x0)/2 and h290f(4)(ϵ) is the error term. The fourth order error term indicating Simpson’s rule will return an exact result when the polynomial in question has a degree of three or less.

Simpson’s Rule Example

Let’s see if Simpson’s rule is more accurate in approximating the following function compared to the result obtained from the Trapezoidal rule.

π40x sin x dx

Approximating the integral with Simpson’s rule:

h=π/402=π8,x1=0+h=π8 π40x sin x dxπ/83[0+0.6011177+0.5553604]=.1513826

Integrating the function over the interval [0,π4] yields π442=0.15175, which gives an approximation error of 0.0003674. Compare this to the approximation found by the trapezoidal rule, 0.2180895, which results in an error value of 0.0663395.

Visualizing how the two methods approximate the integral over the interval gives a good example of why Simpson’s rule is typically more accurate compared to the trapezoidal rule.

# Replicate the polynomial in question in R as a function.
f <- function(x) {
  return(x * sin(x))
}

# Construct the points using the intervals for plotting
df <- data.frame(cbind(c(0, pi/4, pi/4, 0), c(0, f(pi/4), 0, 0)))
curvedf <- data.frame(cbind(c(0, pi/8, pi/4), c(0, f(pi/8), f(pi/4))))


# As Simpson's rule uses the third Lagrange polynomial for approximation, we first 
# interpolate the polynomial using Lagrange's method. The `lagrange.poly()` function 
# is from a previous post on interpolating polynomials with the Lagrange approach, found
# here: http://wp.me/p4aZEo-5RJ

lagrange.poly(curvedf$X1, curvedf$X2)
## [1] "0.058260083543634*x + 0.826137273909778*x**2"


f2 <- function(x) {
  return(0.058260083543634*x + 0.826137273909778*x^2)
}

# Plot the function and the trapezoidal and Simpson's rule approximations.
ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + 
  stat_function(fun = f, color = 'blue') + xlim(c(0,pi/4)) + 
  stat_function(fun = f2) + xlim(c(0,pi/4)) + 
  geom_segment(aes(x = 0, y = 0, xend = pi/4, yend = f(pi/4))) + 
  geom_segment(aes(x = pi/4, y = 0, xend = pi/4, yend = f(pi/4))) + 
  geom_polygon(data = df, aes(x=X1, y=X2), fill = 'blue', alpha = 0.2) + 
  geom_area(stat = 'function', fun = f2, fill = 'black', alpha = 0.4, xlim = c(0, pi/4))

Simpson's rule approximation of function

The blue shaded area represents the approximation of the integral over the interval by the trapezoidal rule while the blue-grayish shaded area is the approximation of Simpson’s rule. The blue curve is the polynomial x sin(x). We can see the approximation given by Simpson’s rule is much more close to the actual definite integral compared to the approximation given by the trapezoidal rule.

We can also write a function to approximate a definite integral using Simpson’s rule:

simpsons.rule <- function(f, a, b) {
  if (is.function(f) == FALSE) {
    stop('f must be a function with one parameter (variable)')
  }
  
  h <- (b - a) / 2
  x0 <- a
  x1 <- a + h
  x2 <- b
  
  s <- (h / 3) * (f(x0) + 4 * f(x1) + f(x2))
  
  return(s)
}

simpsons.rule(f, 0, pi/4)
## [1] 0.1513826
Simpson’s Rule Second Example

Approximate the following definite integral using Simpson’s rule and compare again to the result of the trapezoid rule, which we found in a previous post.

10x2 ex dx
h=(x2x0)2=12,x1=x0+h=0+12=12

Applying Simpson’s rule:

10x2 ex dx1/23[0+0.6065308+0.3678794]=0.1624017

The integral over the interval [0,1] is 25e=0.1606028, thus the error is equal to 0.0017989. The approximation resulting from the trapezoidal rule is 0.1839397, giving an error of 0.0233369.

Using the simpsons.rule() function we created earlier to arrive at the same result:

f2 <- function(x) {
  return(x^2 * exp(-x))
}

simpsons.rule(f2, 0, 1)
## [1] 0.1624017
Composite Simpson’s Rule

As with other Newton-Cotes formulas for approximating definite integrals, Simpson’s rule becomes inaccurate as the integration interval increases and if the nodes are unequally spaced. Similar to the composite trapezoidal rule, the composite approach of Simpson’s rule involves separating the integration interval into an even integer n (the number of subintervals, n in the composite Simpson’s rule approach is required to be even, unlike the composite trapezoidal rule), and then summing each subinterval. More formally, let h=(ba)/n and xj=a+jh for j=0,1,2,,n. The composite Simpson’s rule is then defined as:

baf(x) dx=h3[f(a)+2(n/2)1j=1f(x2j)+4n/2j=1f(x2j1)+f(b)]ba180hff(4)(μ)

Expanding the summations in Simpson’s rule:

baf(x) dx=h3[f(a)+4f(x1)+2f(x2)+4f(x3)+2f(x4)++4f(xn1)+f(b)]

Where aμb. Let’s say we are interested in approximating the following integral using the Composite Simpson’s rule with n=8 subintervals:

20e2x sin 3x dx

Approximating the integral with the Composite Simpson’s rule proceeds as follows:

20e2x sin 3x dx=112[e2(2) sin 3(2)+23j=1e2x2j sin 3(x2j)+44j=1e2x2j1 sin 3(x2j1)+e2(0) sin 3(0)]=14.18334

The following is an implementation of the Composite Simpson’s rule.

composite.simpson <- function(f, a, b, n) {
  if (is.function(f) == FALSE) {
    stop('f must be a function with one parameter (variable)')
  }
  
  h <- (b - a) / n
  
  xj <- seq.int(a, b, length.out = n + 1)
  xj <- xj[-1]
  xj <- xj[-length(xj)]

  approx <- (h / 3) * (f(a) + 2 * sum(f(xj[seq.int(2, length(xj), 2)])) + 4 * sum(f(xj[seq.int(1, length(xj), 2)])) + f(b))
  
  return(approx)
  
}

Replicate the function in R.

f3 <- function(x) {
  return(exp(2 * x) * sin(3 * x))
}

Approximate the definite integral with the composite.simpson() function.

composite.simpson(f3, 0, 2, 8)
## [1] -14.18334
References

Burden, R. L., & Faires, J. D. (2011). Numerical analysis (9th ed.). Boston, MA: Brooks/Cole, Cengage Learning.

Weisstein, Eric W. “Simpson’s Rule.” From MathWorld–A Wolfram Web Resource. http://mathworld.wolfram.com/SimpsonsRule.html

The post Simpson’s Rule for Approximating Definite Integrals in R appeared first on Aaron Schlegel.

To leave a comment for the author, please follow the link and comment on their blog: R – Aaron Schlegel.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)