# Josua Kugler, Christian Merten # a) ----------------------------------------------------------------------- midpoint <- function(f, a, b) (b-a) * f((a+b)/2) trapezoid <- function(f, a, b) (b-a) * (f(a) + f(b))/2 # b) ---------------------------------------------------------------------- nc_integrate <- function(f, lower, upper, n, rule) { xs <- seq(lower, upper, length.out=n+1) sum(rule(f, xs[-(n+1)], xs[-1])) } # c) ------------------------------------------------------------------------ closeable_seq <- function(from, to, len, closed=TRUE) { if (closed) seq(from, to, length.out = len) else seq(from, to, length.out = len+2)[-c(1, len+2)] } newton_cotes <- function(coef, closed=TRUE) { m <- length(coef) w <- coef / sum(coef) function(f, a, b) { t <- mapply(closeable_seq, a, b, len=m, closed=closed) (b-a) * colSums(w*matrix(f(t), ncol=length(a))) } } # d) ------------------------------------------------------------------------ # some objects for param_fun-list sin1x <- function(x) { y <- suppressWarnings(sin(1/x)) y[is.na(y)] <- 0 y } set.seed(0) x <- c(0:1, runif(5)) y <- runif(7) # list of functions with integral interval param_fun <- list( poly = list(f = function(x) x^4 - x^3 - 3*x^2 + x + 2, lower = 0, upper = 2), sin1x = list(f = sin1x, lower = 0, upper = 1), lin = list(f = approxfun(x, y), lower = 0, upper = 1), spline = list(f = splinefun(x, y), lower = 0, upper = 1) ) # true values of integrals true <- c( poly = 0.4, sin1x = 0.504067061906928, lin = 0.472878602164825, spline = 0.97236924451286) # options for creating integral-rules param_rule <- list( midpoint = list(coef = 1, closed=FALSE), trapezoid = list(coef = c(1,1), closed=TRUE), simpson = list(coef = c(1,4,1), closed=TRUE), boole = list(coef = c(7,32,12,32,7), closed=TRUE), open5 = list(coef = c(611, -453, 562, 562, -453, 611), closed=FALSE) ) # how many function evaluations are allowed param_n <- 5*(2:20) library(tidyverse) param <- as_tibble( expand.grid(n = param_n, fun_name = names(param_fun), rule_name = names(param_rule), stringsAsFactors=FALSE)) nc_rules <- sapply(param_rule, do.call, what=newton_cotes) # run nc_integrate with given options call_nc <- function(n, fun_name, rule_name) { opts <- param_fun[[fun_name]] opts$rule = nc_rules[[rule_name]] # to make things fair: # a rule which evaluates f at m points may only be called n/m times opts$n = round(n / length(param_rule[[rule_name]]$coef)) do.call(nc_integrate, opts) } param %>% rowwise() %>% mutate(value=nc_integrate(param_fun[[fun_name]]$f, lower = param_fun[[fun_name]]$lower, upper = param_fun[[fun_name]]$upper, n = n, rule = nc_rules[[rule_name]]), true=true[[fun_name]], error=abs(true-value)) -> res # plot results plots <- lapply(names(param_fun), function(nm) res %>% filter(fun_name == nm) %>% ggplot(aes(x = n, y = error, color = rule_name)) + scale_y_log10() + geom_line() + geom_point() + labs(title = nm) ) gridExtra::grid.arrange(grobs = plots, nrow=2)