======Bisection root-finding in R====== =====Iterative===== The following code was adapted from an original version by Ravi Varadhan posted on the R-help mailing list 20/9/2010. bisectRavi <- function(fn, lo, hi, tol = 1e-7, ...) { flo <- fn(lo, ...) fhi <- fn(hi, ...) if(flo * fhi > 0) stop("root is not bracketed by lo and hi") chg <- hi - lo while (abs(chg) > tol) { mid <- (lo + hi) / 2 fmid <- fn(mid, ...) if (abs(fmid) <= tol) break if (flo * fmid < 0) hi <- mid if (fhi * fmid < 0) lo <- mid chg <- hi - lo } return(mid) } =====Recursive===== The following code was posted to R-help by myself (BioStatMatt) as a recursive alternative to the iterative method above. bisectMatt <- function(fn, lo, hi, tol = 1e-7, ...) { flo <- fn(lo, ...) fhi <- fn(hi, ...) if(flo * fhi > 0) stop("root is not bracketed by lo and hi") mid <- (lo + hi) / 2 fmid <- fn(mid, ...) if(abs(fmid) <= tol || abs(hi-lo) <= tol) return(mid) if(fmid * fhi > 0) return(bisectMatt(fn, lo, mid, tol, ...)) return(bisectMatt(fn, mid, hi, tol, ...)) } =====Illustration===== These functions find the root of a smooth function, provided two 'bracketing' starting values: > testFn <- function(x, a = 1) exp(-x) - a*x > curve(testFn, from = -1, to = 3, xlab = "x", ylab = "testFn(x)") > # Plot the 'bracketing points' > points(c(0, 2),testFn(c(0,2)), pch = "x", col= "red" ) > # Compute and plot the root > abline(h = 0, lty = 2, col = "red") > abline(v = bisectMatt(testFn, 0, 2), lty = 2, col = "red") This code produces the following graphic: {{:bisect.png|Bisection}} =====Timing===== The iterative function is roughly twice faster than the recursive function: > testFn <- function(x, a) exp(-x) - a*x > system.time(replicate(1000, bisectMatt(testFn, 0, 2, a=1))) user system elapsed 0.656 0.000 0.658 > system.time(replicate(1000, bisectRavi(testFn, 0, 2, a=1))) user system elapsed 0.384 0.000 0.393