Notebook 17 – Math 2121, Fall 2020
In today's class we discussed how to compute a line of best fit by finding a least-squares solution to a certain linear system.
Here we explore the problem of finding a polynomial curve of best fit that passes through some given datapoints, which imagine as having the form
xxxxxxxxxx
using Plots
Suppose xvalues = [x1, x2, ..., xn]
. The following method constructs the matrix
This is sometimes called a Vandermonde matrix.
If
is a polynomial satisfying
vandermonde (generic function with 2 methods)
xxxxxxxxxx
function vandermonde(xvalues, m=0)
n = length(xvalues)
m = m == 0 ? n : m
A = zeros(n, m)
for i=1:n
for j=1:m
A[i, j] = xvalues[i]^(j - 1)
end
end
return A
end
4×4 Array{Float64,2}:
1.0 1.0 1.0 1.0
1.0 3.0 9.0 27.0
1.0 4.0 16.0 64.0
1.0 5.0 25.0 125.0
xxxxxxxxxx
vandermonde([1, 3, 4, 5], 4)
Any square Vandermonde matrix is invertible. Hence, there is a unique polynomial of degree
equally_spaced_xvalues (generic function with 1 method)
xxxxxxxxxx
function equally_spaced_xvalues(a, b, n)
n >= 2
b > a
delta = (b - a) / (n - 1)
return [i for i=a:delta:b]
end
-1.0
-0.75
-0.5
-0.25
0.0
0.25
0.5
0.75
1.0
xxxxxxxxxx
equally_spaced_xvalues(-1, 1, 9)
The following method returns the polynomial of best fit (in the sense of least squares) that agrees in the input function fn
at the given xvalues
.
If not given, the degree of the returned polynomial is the length of xvalues
.
fit_polynomial (generic function with 2 methods)
xxxxxxxxxx
function fit_polynomial(fn, xvalues, degree=0)
b = [fn(x) for x=xvalues]
A = vandermonde(xvalues, degree)
coefficients = A \ b
return x -> sum([x^(i - 1) * coefficients[i] for i=1:length(coefficients)])
end
Below are some methods to plot our interpolations.
compare_plots (generic function with 2 methods)
xxxxxxxxxx
function compare_plots(fun, pol, xvalues, title="")
d = 0.001
rng = xvalues[1]:d:xvalues[end]
p = plot(fun, rng, label="function", legend = :outertopright, title=title)
plot!(pol, rng, label="polynomial")
scatter!(xvalues, zeros(1, length(xvalues)),label=false, color=:grey)
return p
end
polynomial_iterpolation_anim (generic function with 2 methods)
xxxxxxxxxx
function polynomial_iterpolation_anim(maxpoints, f, a, b, xvalues, degree=0)
anim = for npoints=[minimum([i maxpoints]) for i=2:maxpoints + 10]
xvals = xvalues(a, b, npoints)
compare_plots(f, fit_polynomial(f, xvals, degree), xvals,
"Degree $(degree == 0 ? npoints - 1 : degree) interpolation with $(npoints) datapoints")
end every 1
gif(anim, "anim_fps15.gif", fps = 2)
end
Here are methods to measure the error of our interpolations.
error (generic function with 1 method)
xxxxxxxxxx
function error(f, g, a, b)
delta = (b - a) / 10000
sum([abs(f(i) - g(i))^2 * delta for i=a:delta:b])
end
plot_log_error (generic function with 2 methods)
xxxxxxxxxx
function plot_log_error(maxpoints, f, a, b, xvalues, degree=0)
plot([
log(error(f, fit_polynomial(f, xvalues(a, b, npoints), degree), a, b))
for npoints = 2:maxpoints
], legend=false, title="Log error in successive polynomial interpolations",
xlabel="degree of interpolation", ylabel="error")
end
Consider the function
#11 (generic function with 1 method)
xxxxxxxxxx
f = x -> 1 / (1 + 25 * x^2)
xxxxxxxxxx
begin
npoints = 9
a, b = -1, 1
equally_spaced = equally_spaced_xvalues(a, b, npoints)
interpolation = fit_polynomial(f, equally_spaced)
compare_plots(
f, interpolation, equally_spaced,
"Polynomial interpolation with $(npoints) datapoints")
end
A major problem involved in polynomial interpolation is overfitting.
We see this for the preceding function when we sample evenly spaced points in the interval
As we add more datapoints and compute higher degree polynomials that fit the data, the 'fit' of these polynomials actually becomes worse!
On other intervals, say
xxxxxxxxxx
polynomial_iterpolation_anim(20, f, a, b, equally_spaced_xvalues)
xxxxxxxxxx
plot_log_error(40, f, a, b, equally_spaced_xvalues)
xxxxxxxxxx
polynomial_iterpolation_anim(12, f, -1, 0, equally_spaced_xvalues)
xxxxxxxxxx
plot_log_error(20, f, -1, 0, equally_spaced_xvalues)
One solution to the problem of overfitting is to fix the degree of our polynomial interpolation. This gives a better error that won't explode as we add more datapoints.
On the other hand, the error may have a nontrivial lower bound and may not tend to zero as we add more data.
If the number of datapoints
where
20
xxxxxxxxxx
fixed_degree = 20
xxxxxxxxxx
polynomial_iterpolation_anim(45, f, a, b, equally_spaced_xvalues, fixed_degree)
xxxxxxxxxx
plot_log_error(60, f, a, b, equally_spaced_xvalues, fixed_degree)
Another solution to the overfitting problem is to sample our input function at different
chebyshev_spaced_xvalues (generic function with 1 method)
xxxxxxxxxx
function chebyshev_spaced_xvalues(a, b, n)
n >= 2
b > a
cheb = [cos((2 * k - 1) / (2 * n) * pi) for k=n:-1:1]
return [a + (i + 1) * (b - a) / 2 for i=cheb]
end
The
However, if we sample our function at these points, the resulting polynomial interpolation is much more accurate, and we avoid overfitting as the degree of our interpolation increases:
xxxxxxxxxx
polynomial_iterpolation_anim(30, f, a, b, chebyshev_spaced_xvalues)
xxxxxxxxxx
plot_log_error(30, f, a, b, chebyshev_spaced_xvalues)
The error here is better than what we get by fixing the degree of our interpolation and using least-squares.
What are the numbers chebyshev_spaced_xvalues(a, b, n)
?
The Chebyshev polynomials
These are the unique polynomials that satisfy
chebyshev (generic function with 1 method)
xxxxxxxxxx
function chebyshev(n)
if n == 0
return x -> 1
elseif n == 1
return x -> x
else
return x -> 2 * x * chebyshev(n - 1)(x) - chebyshev(n - 2)(x)
end
end
xxxxxxxxxx
begin
p = plot(title="Chebyshev polynomials of the first kind")
for i=1:5
plot!(chebyshev(i), -1, 1, label="T_$(i)(x)", legend=:outerright)
end
plot(p)
end
The values returned by chebyshev_spaced_xvalues(-1, 1, n)
are the roots of
plot_chebyshev (generic function with 1 method)
xxxxxxxxxx
function plot_chebyshev(n)
q = plot(chebyshev(n), -1, 1,
title="Plot of T_$(n)(x) and Chebyshev-spaced x-values", legend=:outerright)
xvalues = chebyshev_spaced_xvalues(-1, 1, n)
scatter!(xvalues, zeros(1, n), legend=false, color=:grey)
plot(q)
end
xxxxxxxxxx
plot_chebyshev(20)
It can be shown that using chebyshev_spaced_xvalues(a, b, n)
to sample our input function yields the best possible exact polynomial interpolation of degree
See https://en.wikipedia.org/wiki/Chebyshev_nodes for more information.