= function ( x ) { 1 / ( 1 + x ^ 2 ) } rfun
Esempio di Runge
Appunti di calcolo numerico
Approssimazione di funzioni continue
Diamo di seguito gli enunciati di alcuni teoremi classici sulla approssimazione di funzioni continue tramite polinomi. Il primo teorema ci assicura che i polinomi sono densi nelle funzioni continue. Ovvero che si può approssimare arbitrariamente bene una funzione continua con un (solo) polinomio in un intervallo [a,b].
Nel caso di funzioni a valori reali Bernstein da una dimostrazione costruttiva, ovvero da un algoritmo per la costruzione del polinomio approssimante.
Nel teorema di Bernstein il polinomio ha una costruzione ad hoc, nel caso di polinomi interpolanti le cose non vanno cosi bene come si deduce dal seguente teorema:
Per far fallire l’interpolazione nel teorema di Faber bisogna costruire delle funzioni molto particolari. Gia se la funzione è è abbastanza regolare ad esempio C^1[a,b] (cioè è derivabile) allora le cose vanno meglio. Vediamo che per funzioni molto regolari si può dare una stima molto precisa dell’errore di interpolazione:
Per migliorare l’interpolazione non abbiamo il controllo di f^{(n+1)}(\zeta) ma possiamo cercare di controllare il termine (x-x_0)(x-x_1)\cdots(x-x_n) scegliendo opportunamente i nodi di interpolazione. Ci sono disctribuzioni di nodi che minimizzano \max_{x\in I(x_0,x_1,\ldots,x_n)}|(x-x_0)(x-x_1)\cdots(x-x_n)| e sono i nodi di Chebyshev.
Scegliendo i nodi di Chebichev per una funzione C^1[a,b] abbiamo il seguente risultato
Controesempio di Runge
Carl David Tolmé Runge
Anche se la funzione è molto regolare (ad esempio C^\infty[a,b]) sono necessary i nodi di Chebyshev per avere la convergenza. Il seguente controesempio lo trovate in
- Carl Runge, Über empirische Funktionen und die Interpolation zwischen äquidistanten Ordinaten, Zeitschrift für Mathematik und Physik, 1901, vol. 46, pages 224-243
Innanzitutto costruiamo la funzione da interpolare f(x) = 1/(1+x^2):
e salviamo un certo numero di punti per plottare la funzione
= seq(from = -5, to = 5, by = 0.01) x
plotta la funzione da interpolare
= rfun( x );
y
# salva il plot in una funzione per riusarla piu avanti
= function() {
plot_runge plot(
type="l",lwd=2,col=4,
x,y,main="Funzione di Runge",
xlim=c(-5,5), ylim=c(-1,4),
xlab="x", ylab="y", asp=1
)# Turn on grid
grid(
lty = 2, # Grid line type
col = "lightgray", # Grid line color
lwd = 1
)
}plot_runge()
costruiamo un primo polinomio interpolante
library(polynom)
# punti di interpolazione
= seq(from = -5, to = 5, by = 2)
x1 = rfun( x1 )
y1
# costruzione del polinomio interopolante
= poly.calc(x1, y1)
poly1
# polinomio interpolante come funzione
= as.function(poly1)
fun1
# valutazione del polinomio
= fun1(x)
yy1
# plotta funzione di Runge
plot_runge()
# punti di interpolazione
points(x1, y1)
lines(x, yy1, type = "l", lty = 1, col=6)
ne aggiungiamo un secondo
library(polynom)
# punti di interpolazione
= seq(from = -5, to = 5, by = 1)
x2 = rfun( x2 )
y2
# costruzione del polinomio interopolante
= poly.calc(x2, y2)
poly2
# polinomio interpolante come funzione
= as.function(poly2)
fun2
# valutazione del polinomio
= fun2(x)
yy2
# plotta funzione di Runge
plot_runge()
# punti di interpolazione
points(x2, y2)
lines(x, yy1, type = "l", lty = 1, col=6)
lines(x, yy2, type = "l", lty = 1, col=7)
aggiunge una serie di polinomi di grado crescente
library(polynom)
# plotta funzione di Runge
plot_runge()
= 1;
kk for ( delta in c(2,1,0.7,0.5) ) {
= seq(from = -5, to = 5, by = delta)
xk = rfun( xk )
yk = poly.calc(xk, yk)
polyk = as.function(polyk)
funk = funk(x)
yyk lines(x, yyk, type = "l", lty = 1, col=6+kk)
= kk+1
kk }
si vede chiaramente che man mano che aumentano i punti di interpolazione i polinomi approssimano sempre peggio la funzione f(x) = 1/(1+x^2). Ma usando i nodi di Chebichev si vede che le cose meglio:
library(polynom)
# plotta funzione di Runge
plot_runge()
= 1;
kk for ( n in c(4,6,8,10,12) ) {
= seq(from = 1, to = n+1, by = 1)/(n+2)
tk = 5*cos(pi*tk)
xk = rfun( xk )
yk = poly.calc(xk, yk)
polyk = as.function(polyk)
funk = funk(x)
yyk lines(x, yyk, type = "l", lty = 1, col=6+kk)
= kk+1
kk }
ma fino ad un certo punto. Se aumentiamo troppo il grado del polinomio subentrano gli errori floating poing che si accumulano al punto da rendere totalmente errata la valutazione:
library(polynom)
# plotta funzione di Runge
plot_runge()
= 1;
kk for ( n in c(12,15,17,20) ) {
= seq(from = 1, to = n+1, by = 1)/(n+2)
tk = 5*cos(pi*tk)
xk = rfun( xk )
yk = poly.calc(xk, yk)
polyk = as.function(polyk)
funk = funk(x)
yyk lines(x, yyk, type = "l", lwd=2, lty = 1, col=6+kk)
= kk+1
kk
}# Add a legend
legend(4, 3, legend=c("n=12", "n=15", "n=17", "n=20"),
col=c(7,8,9,10), lty=1, cex=0.8)
Conclusioni
I polinomi sono densi nelle funzioni continue ma approssimare una funzione continua con polinomi interpolanti non è facile.
Anche se la funzione è regolare i nodi di interpolazione vanno scelti con cura per ridurre l’errore (vedi teorema di Cauchy)
Usando i nodi di Chebyshev teoricamente per funzioni regolari si ha una covergenza uniforme del polinomio interpolante alla funzione da approssimare.
In ogni caso la valutazione di un polinomio è instabile e sopra gradi moderatamente alti (grado 10) non si dovrebbe usare un polinomio per approssimare una funzione.
Si possono usare polinomi a tratti (spline) di grado basso per approssiamare funzioni continue
Per migliorare la valutazione di un polinomio di grado si possono usare polinomi speciali (polinomi ortogonali con le loro ricorrenze)