library(tidyverse)
Primjer 1. Odredite interpolacijski polinom kroz točke \((-2,0)\), \((-1,1)\), \((1,2)\) i \((2,0)\).
podaci <- tibble(x = c(-2, -1, 1, 2),
y = c(0, 1, 2, 0))
Pomoću naredbe lm možemo dobiti koeficijente
interpolacijskog polinoma.
model1 <- lm(y ~ 1 + x + I(x^2) + I(x^3), data = podaci)
coef(model1)
## (Intercept) x I(x^2) I(x^3)
## 2.0000000 0.6666667 -0.5000000 -0.1666667
U općenitijem slučaju je jednostavnije unijeti formulu pomoću naredbe
poly. Opcija raw = TRUE daje koeficijente s
obzirom na kanonsku bazu, a raw = FALSE daje koeficijente s
obzirom na pripadnu ortogonalnu bazu.
model2 <- lm(y ~ poly(x, 3, raw = TRUE), data = podaci)
coef(model2)
## (Intercept) poly(x, 3, raw = TRUE)1 poly(x, 3, raw = TRUE)2
## 2.0000000 0.6666667 -0.5000000
## poly(x, 3, raw = TRUE)3
## -0.1666667
Ako tražimo polinom čiji stupanj je veći lili jednak od broja točaka, onda uz veće potencije koeficijenti nisu određeni jer je premalo zadanih točaka.
model3 <- lm(y ~ poly(x, 10, raw = TRUE), data = podaci)
coef(model3)
## (Intercept) poly(x, 10, raw = TRUE)1 poly(x, 10, raw = TRUE)2
## 2.0000000 0.6666667 -0.5000000
## poly(x, 10, raw = TRUE)3 poly(x, 10, raw = TRUE)4 poly(x, 10, raw = TRUE)5
## -0.1666667 NA NA
## poly(x, 10, raw = TRUE)6 poly(x, 10, raw = TRUE)7 poly(x, 10, raw = TRUE)8
## NA NA NA
## poly(x, 10, raw = TRUE)9 poly(x, 10, raw = TRUE)10
## NA NA
ggplot(podaci, aes(x = x, y = y)) +
geom_smooth(method = "lm", formula = y ~ poly(x, 3, raw = TRUE), se = FALSE, fullrange = TRUE) +
geom_point(size = 2.5, color = "red", shape = 21, fill = "yellow") +
ggtitle("interpolacijski polinom") + xlim(-4, 2.5)
Vrijednosti u kojima želimo napraviti predikcije.
vr <- c(-3, -1.5, -0.5, 0.5, 1)
Predikcije preko model1
pr1 <- predict(model1, tibble(x = vr))
names(pr1) <- vr
pr1
## -3 -1.5 -0.5 0.5 1
## -4.440892e-16 4.375000e-01 1.562500e+00 2.187500e+00 2.000000e+00
Predikcije preko model2
pr2 <- predict(model2, tibble(x = vr))
names(pr2) <- vr
pr2
## -3 -1.5 -0.5 0.5 1
## -4.440892e-16 4.375000e-01 1.562500e+00 2.187500e+00 2.000000e+00
Predikcije preko model3. Upozorava nas na neodređene
koeficijente uz veće potencije zbog premalo točaka, no predikcije dobro
funkcioniraju.
pr3 <- predict(model3, tibble(x = vr))
## Warning in predict.lm(model3, tibble(x = vr)): prediction from a rank-deficient
## fit may be misleading
names(pr3) <- vr
pr3
## -3 -1.5 -0.5 0.5 1
## -4.440892e-16 4.375000e-01 1.562500e+00 2.187500e+00 2.000000e+00
Promatramo funkciju \(f(x)=\dfrac{1}{1+x^2}\) na segmentu \([-5,5]\).
zvono <- as_mapper(~1 / (1 + .x^2))
Definiramo ekvidistantne razdiobe segmenta \([-5,5]\) s \(5\), \(10\), \(15\) i \(20\) točaka.
razdioba <- list()
for (i in c(5,10,15,20)) {
razdioba[[as.character(i)]] <- list(x = seq(from = -5, to = 5, length.out = i),
y = zvono(seq(from = -5, to = 5, length.out = i))
)
}
razdioba <- razdioba %>% map(as_tibble) %>% map_dfr(rbind, .id = "broj") %>%
mutate(broj = fct_relevel(broj, "5"))
Vidimo da interpolacijski polinom većeg stupnja loše aproksimira funkciju \(f\) blizu rubova segmenta \([-5,5]\).
ggplot(razdioba, aes(x = x, y = y)) +
stat_function(fun = zvono, color = "red") +
geom_smooth(method = "lm", formula = y ~ poly(x, 19, raw = TRUE),
se = FALSE, fullrange = TRUE, linewidth = 0.7) +
geom_point(size = 2, color = "red", shape = 21, fill = "yellow") +
facet_wrap(vars(broj), scales = "free_y")
Čebiševljev polinom stupnja \(n\) je funkcija \[T_n:[-1,1]\to\mathbb{R},\quad T_n(x)=\cos{(n\arccos{x})},\quad n=0,1,2,\dotsc\] Nultočke od \(T_n\) su dane s \(x_k=\cos{\dfrac{2k+1}{2n}\pi},\quad k=0,1,2,\dotsc,n-1\).
Ako interpoliramo neku funkciju na segmentu \([-1,1]\), za čvorove interpolacije možemo uzeti i nultočke nekog Čebiševljevog polinoma (ne moraju čvorovi uvijek biti dobiveni podjelom segmenta na \(n\) jednakih dijelova, nego to mogu biti bilo koje točke unutar zadanog segmenta). Međutim, ako imamo proizvoljni segment \([a,b]\), tada nultočke Čebiševljevog polinoma možemo transformirati na taj segment pomoću linearne transformacije \[\varphi:[-1,1]\to[a,b],\qquad \varphi(x)=\frac{b-a}{2}x+\frac{b+a}{2}\] i za čvorove interpolacije uzeti točke \[\varphi(x_k),\quad k=0,1,\dotsc,n-1\] gdje su \(x_k\) nultočke Čebiševljevog polinoma \(T_n\).
Definiramo Čebiševljeve razdiobe segmenta \([-5,5]\) s \(5\), \(10\), \(15\) i \(20\) točaka.
cheb_nul <- list()
for (i in c(5,10,15,20)) {
cheb_nul[[as.character(i)]] <- cos((2*(0:(i-1)) + 1) * pi / (2 * i))
}
a <- -5
b <- 5
cheb_razdioba <- map(cheb_nul, ~(b - a) / 2 * .x + (b + a) / 2)
cheb_razdioba <- cheb_razdioba %>% map(as_tibble) %>%
map_dfr(rbind, .id = "broj") %>% rename(x = value) %>%
mutate(broj = fct_relevel(broj, "5"), y = zvono(x))
U ovom slučaju interpolacijski polinom većeg stupnja dobro aproksimira funkciju \(f\) na segmentu \([-5,5]\).
ggplot(cheb_razdioba, aes(x = x, y = y)) +
stat_function(fun = zvono, color = "red") +
geom_smooth(method = "lm", formula = y ~ poly(x, 19, raw = TRUE),
se = FALSE, fullrange = TRUE, linewidth = 0.7) +
geom_point(size = 2, color = "red", shape = 21, fill = "yellow") +
facet_wrap(vars(broj), scales = "free_y")
Napravimo ekvidistantnu razdiobu segmenta \([-5,5]\) s \(10\) točaka i pripadnim vrijednostima funkcije \(f(x)=\dfrac{1}{1+x^2}\) u tim točkama.
tocke <- tibble(x = seq(from = -5, to = 5, length = 10),
y = zvono(x))
tocke
## # A tibble: 10 × 2
## x y
## <dbl> <dbl>
## 1 -5 0.0385
## 2 -3.89 0.0620
## 3 -2.78 0.115
## 4 -1.67 0.265
## 5 -0.556 0.764
## 6 0.556 0.764
## 7 1.67 0.265
## 8 2.78 0.115
## 9 3.89 0.0620
## 10 5 0.0385
Tražimo prirodni kubični splajn na danim podacima tako da generiramo \(100\) vrijednosti tog splajna na segmentu \([-5,5]\). Prirodni kubični splajn u rubnim točkama segmenta ima druge derivacije jednake nula.
natur <- spline(tocke$x, tocke$y, n = 100, method = "natural") %>% as_tibble()
natur
## # A tibble: 100 × 2
## x y
## <dbl> <dbl>
## 1 -5 0.0385
## 2 -4.90 0.0398
## 3 -4.80 0.0411
## 4 -4.70 0.0425
## 5 -4.60 0.0441
## 6 -4.49 0.0458
## 7 -4.39 0.0477
## 8 -4.29 0.0499
## 9 -4.19 0.0524
## 10 -4.09 0.0552
## # … with 90 more rows
ggplot(natur, aes(x, y)) +
stat_function(fun = zvono, n = 180, aes(color = "zvono")) +
geom_line(aes(color = "prirodni splajn")) +
geom_point(data = tocke, size = 2, color = "red", shape = 21, fill = "yellow") +
scale_color_manual("Graf", values = c("zvono" = "red", "prirodni splajn" = "blue"))
fmm metoda daje kubični splajn koji u rubnim točkama
segmenta ima treće derivacije jednake trećim derivacijama kubičnih
polinoma koje dobijemo tako da tražimo interpolacijske polinome kroz
prve četiri točke razdiobe segmenta i kroz zadnje četiri točke razdiobe
segmenta.
fmm <- spline(tocke$x, tocke$y, n = 100, method = "fmm") %>% as_tibble()
fmm
## # A tibble: 100 × 2
## x y
## <dbl> <dbl>
## 1 -5 0.0385
## 2 -4.90 0.0400
## 3 -4.80 0.0415
## 4 -4.70 0.0430
## 5 -4.60 0.0446
## 6 -4.49 0.0463
## 7 -4.39 0.0482
## 8 -4.29 0.0503
## 9 -4.19 0.0527
## 10 -4.09 0.0554
## # … with 90 more rows
ggplot(fmm, aes(x, y)) +
stat_function(fun = zvono, n = 180, aes(color = "zvono")) +
geom_line(aes(color = "fmm splajn")) +
geom_point(data = tocke, size = 2, color = "red", shape = 21, fill = "yellow") +
scale_color_manual("Graf", values = c("zvono" = "red", "fmm splajn" = "blue"))
Iako sa slika ne vidimo veliku razliku između prirodnog i fmm splajna, ipak to nisu iste funkcije ako pogledamo njihove vrijednosti u gornjim tablicama. Možemo definirati splajnove kao funkcije i računati vrijednosti u željenim točkama.
spl1 <- splinefun(tocke$x, tocke$y, method = "natural")
spl2 <- splinefun(tocke$x, tocke$y, method = "fmm")
Možemo vidjeti da na primjer u točki \(4.9\) ova dva splajna nemaju iste vrijednosti. Razlika tih vrijednosti je jako mala pa ih na slici nije lako uočiti osim ako jako zumiramo sliku.
c(spl1(4.9), spl2(4.9))
## [1] 0.03975041 0.03996341
ggplot() +
geom_line(data = fmm, aes(x, y, color = "fmm splajn")) +
geom_line(data = natur, aes(x, y, color = "prirodni splajn")) +
scale_color_manual("Graf", values = c("prirodni splajn" = "red", "fmm splajn" = "blue")) +
xlim(4.19,5) + ylim(0.035,0.055)
Što je veći broj točaka u razdiobi segmenta \([-5,5]\), kubični splajn bolje aproksimira funkciju \(f(x)=\dfrac{1}{1+x^2}\). Pogledajmo to na primjeru prirodnog splajna.
splajn <- razdioba %>% group_split(broj) %>% map(~splinefun(.$x, .$y, method = "natural"))
names(splajn) <- levels(razdioba$broj)
x_spl <- rep(with(razdioba, seq(min(x), max(x), length = 100)), times = length(splajn))
broj_spl <- rep(names(splajn), each = 100)
y_spl <- map2_dbl(x_spl, broj_spl, ~splajn[[.y]](.x))
tab_spl <- tibble(broj = fct_relevel(broj_spl, "5"), x = x_spl, y = y_spl)
ggplot(razdioba, aes(x = x, y = y)) +
stat_function(fun = zvono, color = "red") +
geom_line(data = tab_spl, aes(x = x, y = y), color = "blue") +
geom_point(size = 1.5, color = "red", shape = 21, fill = "yellow") +
facet_wrap(vars(broj), scales = "free_y") +
ggtitle("Interpolacija s prirodnim splajnom")
Primjer 2. Odredite prirodni kubični splajn za podatke dane u donjoj tablici.
| x | 0 | 2.0 | 2.5 | 3.0 | 3.5 | 4.0 | 4.5 | 5.0 | 6 |
| y | 0 | 2.9 | 3.5 | 3.8 | 3.5 | 3.5 | 3.5 | 2.6 | 0 |
Zadane točke i pripadni prirodni kubični splajn
tocke1 <- tibble(x = c(0, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6),
y = c(0, 2.9, 3.5, 3.8, 3.5, 3.5, 3.5, 2.6, 0))
natur1 <- spline(tocke1$x, tocke1$y, n = 100, method = "natural") %>% as_tibble()
Graf pripadnog prirodnog kubičnog splajna
ggplot(natur1, aes(x, y)) +
geom_line(color = "blue") +
geom_point(data = tocke1, size = 2, color = "red", shape = 21, fill = "yellow")
Primjer 3. Odredite prirodni kubični splajn za podatke dane u donjoj tablici.
| x | 0 | 1 | 2 | 3 | 4 |
| y | 0 | 0 | 1 | 0 | 0 |
Zadane točke i pripadni prirodni kubični splajn
tocke2 <- tibble(x = c(0, 1, 2, 3, 4),
y = c(0, 0, 1, 0, 0))
natur2 <- spline(tocke2$x, tocke2$y, n = 100, method = "natural") %>% as_tibble()
Graf pripadnog prirodnog kubičnog splajna
ggplot(natur2, aes(x, y)) +
geom_line(color = "blue") +
geom_point(data = tocke2, size = 2, color = "red", shape = 21, fill = "yellow")
Kubični splajn u čvorovima ima neprekidne derivacije do drugog reda, dok po dijelovima kubična Hermiteova interpolacija u čvorovima ima samo neprekidnu derivaciju prvog reda. Uz zadane čvorove i funkcijske vrijednosti moramo zadati i vrijednosti derivacije u čvorovima.
Pogledajmo sve na primjeru funkcije \(f(x)=\dfrac{1}{1+x^2}\) na segmentu \([-5,5]\) sa \(10\) ekvidistantnih čvorova.
der_zvono <- D(expression(1 / (1 + x^2)), "x")
tocke <- tocke %>% mutate(dy = eval(der_zvono))
herm <- splinefunH(tocke$x, tocke$y, tocke$dy)
herm_tocke <- tibble(x = seq(-5, 5, length = 150),
y = herm(x),
d1 = herm(x, deriv = 1),
d2 = herm(x, deriv = 2))
ggplot(herm_tocke, aes(x, y)) +
stat_function(fun = zvono, n = 180, aes(color = "zvono")) +
geom_line(aes(color = "Hermite")) +
geom_point(data = tocke, size = 2, color = "red", shape = 21, fill = "yellow") +
scale_color_manual("Graf", values = c("zvono" = "red", "Hermite" = "forestgreen"))