Here’s another approach that also lets you specify the maximum absolute error between the piecewise linear interpolant and the original function. It uses the amazing ApproxFun
package.
"""
piecewise(fun, a, b, maxerr)
Return a vector of points on the interval `[a,b]` at which a piecewise linear interpolant
to the function `fun` will yield a maximum absolute error at most `maxerr`, using the `ApproxFun` package.
"""
function piecewise(fun, a, b, maxerr)
dom = Domain(a..b)
f = Fun(fun, dom)
ya, yb = fun(a), fun(b)
m = (yb-ya)/(b-a)
flinear = Fun(x -> m*(x-a) + ya, dom)
extr = extrema(flinear-f)
err = maximum(abs.(extr))
if err ≤ maxerr
[a,b]
else
l = piecewise(fun, a, (a+b)/2, maxerr)
r = piecewise(fun, (a+b)/2, b, maxerr)
vcat(l, r[2:end]) # eliminate duplicate midpoint
end
end
Here is an example of its use:
using ApproxFun
using Plots
f(x) = x*sin(1/x)
fun = f
a, b = 0.03, 0.2
maxerr = 0.002
knots = piecewise(fun, a, b, maxerr)
fknots = fun.(knots)
plot(range(a, b, length=500), fun, label = "Original Function")
scatter!(knots, fknots, label = "Interp. Knots")
plot!(knots, fknots, label = "Linear Interp.")