Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

obrazek

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

obrazek

Interpolacja

Interpolacja wielomianowa

SPIS TREŚCI REMANENT
Podrozdziały
 

Algorytm

W interpolacji wielomianowej przybliżamy funkcję f(x) wielomianem WNN-1(x), gdzie NN - 1 to stopień wielomianu równy liczbie węzłów interpolacji NN pomniejszonej o 1. Aby zrozumieć, jak to działa, przeanalizujmy kilka przykładów.

Dwa węzły interpolacyjne

Mamy pewną funkcję y = f(x), w której są wyznaczone dwa węzły - (x0,y0) i (x1,y1): y0 = f(x0); y1 = f(x1).

Ponieważ NN = 2 (liczba węzłów), to funkcję f(x) będziemy interpolować wielomianem pierwszego stopnia, czyli dwumianem liniowym:

Mamy przepis dwumianu W1(x), musimy znaleźć wartości współczynników a1 i a0.

Wykorzystamy fakt, iż w węzłach funkcja f(x) oraz dwumian W1(x) przyjmują tę samą wartość. Otrzymujemy układ dwóch równań, gdzie niewiadomymi są wartości współczynników dwumianu:

Wykorzystamy wzory wyprowadzone w rozdziale o układzie 2 równań liniowych:

Gdy mamy wyliczone współczynniki, możemy napisać wzór na wielomian interpolujący:

Otrzymaliśmy wzór równania prostej przechodzącej przez oba węzły (x0,y0) i (x1,y1):

Teraz wartości funkcji f(x) przybliżamy wartościami wielomianu. W tej postaci jest to dokładny odpowiednik interpolacji liniowej.

Trzy węzły interpolacyjne

Jeśli mamy trzy węzły interpolacyjne, to postępujemy w sposób identyczny:

Dla trzech węzłów (x0,y0), (x1,y1), (x2,y2), NN = 3:

W trójmianie kwadratowym mamy trzy współczynniki: a0, a1 i a2. Aby je wyznaczyć, tworzymy układ trzech równań, gdzie niewiadomymi są wartości tych współczynników:

Układ rozwiązujemy za pomocą jednej z metod rozwiązywania układów równań liniowych, np. Gausa-Crouta i otrzymujemy wartości współczynników trójmianu. Wartości funkcji interpolujemy wartościami trójmianu.

Więcej węzłów interpolacyjnych

Przy większej liczbie węzłów zasada jest wciąż identyczna. Wielomian interpolacyjny ma postać:

Jeśli mamy NN węzłów interpolacyjnych, tworzymy układ NN równań liniowych, gdzie niewiadomymi są współczynniki wielomianu interpolacyjnego, a współczynnikami to potęgi współrzędnej xi węzła oraz współrzędnej yi jako wyraz wolny:

Układ równań rozwiązujemy i otrzymujemy współczynniki ai wielomianu. Wielomian używamy do obliczania przybliżonej wartości funkcji.

Im więcej węzłów, tym dokładniej można wyliczać wartość funkcji. Na pierwszy rzut oka wszystko wygląda wspaniale. Układy równań liniowych możemy efektywnie obliczać. Tu nie ma problemów. Czyli wielomian interpolacyjny dostaniemy dosyć łatwo. Jednakże przy dużej liczbie węzłów otrzymujemy wielomian o wysokim stopniu, który zawiera wysokie potęgi x. Takie potęgi są niestabilne numerycznie - nawet drobna zmiana x, może dawać dużą zmianę wartości potęgi, np. załóżmy, że mamy we wzorze x20:

Zauważ, iż drobna zmiana wartości x o 0,001, daje zmianę o ponad 382 miliardy. W efekcie wartość wielomianu interpolacyjnego może daleko odbiegać od wartości funkcji pomiędzy węzłami. Nosi to nazwę efektu Rungego.

Z tego powodu matematycy szukali innych rozwiązań problemu interpolacji. Zwróć uwagę, iż przy wyprowadzaniu wielomianu interpolującego nie zakładaliśmy nic na temat rozkładu węzłów. Mogą one być podane w dowolnej kolejności i nie muszą być rozłożone równomiernie w przedziale interpolacji (muszą jedynie być różne). W rzeczywistości rozkład równoodległy węzłów jest nawet niekorzystny z uwagi na efekt Rungego. Przy stałej odległości między węzłami i wysokim stopniu wielomianu, na krańcach przedziału pojawiają się gwałtowne oscylacje. Aby temu zapobiec, stosuje się zagęszczanie węzłów blisko krańców przedziału


do podrozdziału  do strony 

Przykładowa implementacja

Program interpoluje funkcję:

 

Przedział <-π/2;π/2>. W przedziale program generuje 6 pseudolosowych współrzędnych x bez powtórzeń, wylicza dla nich współrzędną y wg przepisu funkcji i dostaje współrzędne 6 węzłów interpolacyjnych. Tworzy układ 6 równań liniowych z wartościami współrzędnych węzłów i wylicza wartości współczynników wielomianu interpolującego:

W programie powstaje układ 6 równań, w których niewiadomymi są współczynniki wielomianu interpolującego:

Macierz rozszerzona układu jest następująca:

C++
// Interpolacja wielomianowa
// (C)2026 mgr Jerzy Wałaszek
// Metody numeryczne 0074
//---------------------------

#include <iostream>
#include <windows.h>
#include <iomanip>
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

// STAŁE
//------

// Przedział interpolacji
// od XS do XE
const double XS = -M_PI / 2;
const double XE =  M_PI / 2;
// liczba węzłów
const int NN = 6;
// Liczba interpolowanych
// punktów
const int N = 10;
// Dokładność przyrównania do 0
const double EPS = 0.00001;

// Tablice
//--------

// Współrzędne x węzłów
double nx[NN];
// Współrzędne y węzłów
double ny[NN];
// Współczynniki wielomianu
double a[NN];

// Funkcje
//--------

// Funkcja interpolowana
//----------------------
double f(double x)
{
  return sin(x);
}

// Funkcja zwraca wartość
// pseudolosową w przedziale
// <0,1)
//--------------------------
double random()
{
  return rand() /
         ((double)RAND_MAX + 1);
}

// Funkcja wyznacza węzły
// dla wielomianu
//-----------------------
void set_nodes()
{
  // Dwa pierwsze węzły
  // definiują przedział
  // interpolacji
  nx[0] = XS;
  nx[1] = XE;

  // Pozostałe punkty są
  // rozsiane po przedziale
  int i = 2,j;
  double xx,dx;

  // minimalna odległość w osi OX
  // pomiędzy węzłami
  dx = (XE - XS) / 2 / NN;
  while(i < NN)
  {
    xx = XS + random() * (XE - XS);
    bool t = false;
    for(j = 0; j < i; j++)
      // Sprawdzamy odległość
      // węzłów w osi OX
      if(fabs(xx - nx[j]) < dx)
      {
        t = true;
        break;
      }
    // Węzeł za blisko innego,
    // losujemy go ponownie
    if(t) continue;
    nx[i++] = xx;
  }

  // Dla współrzędnych x
  // liczymy współrzędne y
  for(i = 0; i < NN; i++)
    ny[i] = f(nx[i]);
}

// Oblicza wartość wielomianu
//---------------------------
double f_i(double x)
{
  // Współczynnik przy x^(NN-1)
  double w = a[NN - 1];
  int i;
  for(i = NN-1; i > 0; i--)
  {
    w *= x;
    w += a[i-1];
  }
  return w;
}

// Funkcja rozwiązuje układ równań
// metodą eliminacji Gaussa
// NN - stopień macierzy
// AB - macierz rozszerzona NN x (NN + 1)
// X  - macierz rozwiązań NN x 1
// Jeśli układ został rozwiązany,
// to wynikiem jest true.
// Inaczej wynikiem jest false.
//--------------------------------
bool GaussCrout(double AB[][NN+1],
                double X[NN])
{
  int i,j,k;
  double m,s;
  int WK[NN + 1];

  // Przygotowujemy wektor
  // kolumnowy
  for(i = 0; i <= NN; i++)
    WK[i] = i;

  // Eliminacja
  for(i = 0; i < NN - 1; i++)
  {
    for(k = i, j = i + 1; j < NN; j++)
      if(fabs(AB[i][WK[k]]) <
         fabs(AB[i][WK[j]]))
        k = j;

    if(k != i)
      swap(WK[k],WK[i]);

    if(fabs(AB[i][WK[i]]) < EPS)
    {
      return false;
    }

    for(j = i + 1; j < NN; j++)
    {
      m = -AB[j][WK[i]] / AB[i][WK[i]];
      for(k = i + 1; k <= NN; k++)
        AB[j][WK[k]] += m * AB[i][WK[k]];
    }
  }

  // Obliczanie wartości niewiadomych
  for(i = NN - 1; i >= 0; i--)
  {
    if(fabs(AB[i][WK[i]]) < EPS)
    {
      return false;
    }
    s = AB[i][NN];
    for(j = NN - 1; j > i; j--)
      s -= AB[i][WK[j]] * X[WK[j]];
    X[WK[i]] = s / AB[i][WK[i]];
  }

  return true;
}

// Program główny
//---------------
int main()
{
  int i,j;

  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  cout << setprecision(4)
       << fixed;

  // Inicjujemy generator
  // pseudolosowy
  srand(time(nullptr));

  // Generujemy węzły
  set_nodes();

  // Tworzymy macierz
  // układu równań
  double ab[NN][NN+1];

  for(i = 0; i < NN; i++)
  {
    // W ostatniej kolumnie
    // są współrzędne y
    ab[i][NN] = ny[i];
    // W pierwszej są 1
    ab[i][0] = 1;
    // Pozostałe są potęgami
    for(j = 1; j < NN; j++)
      ab[i][j] = nx[i] * ab[i][j-1];
  }

  // Wyznaczamy współczynniki
  // wielomianu
  if(GaussCrout(ab,a))
  {
    cout <<
    "Interpolacja wielomianowa\n"
    "-------------------------\n\n";

    // Generujemy N punktów x
    // i liczymy dla nich y
    // przy pomocy wielomianu
    // i wyświetlamy wynik
    double x,y;
    for(i = 0; i < N; i++)
    {
      x = XS + random() * (XE - XS);
      y = f_i(x);
      cout << "x = "
           << setw(7) << x
           << ", f(x) = "
           << setw(7) << f(x)
           << ", interpolacja f(x) = "
           << setw(7) << y
           << endl;
    }
  }
  else
    cout <<
    "Błąd w trakcie obliczeń!!!\n";
  cout << endl;
  system("pause");
  return 0;
}
Wynik:
Interpolacja wielomianowa
-------------------------

x =  1.3609, f(x) =  0.9781, interpolacja f(x) =  0.9780
x =  0.0129, f(x) =  0.0129, interpolacja f(x) =  0.0129
x = -0.1596, f(x) = -0.1590, interpolacja f(x) = -0.1589
x = -0.1612, f(x) = -0.1605, interpolacja f(x) = -0.1605
x = -1.5022, f(x) = -0.9977, interpolacja f(x) = -0.9975
x =  1.1324, f(x) =  0.9054, interpolacja f(x) =  0.9056
x = -0.4658, f(x) = -0.4491, interpolacja f(x) = -0.4491
x = -0.5882, f(x) = -0.5549, interpolacja f(x) = -0.5549
x = -0.7689, f(x) = -0.6954, interpolacja f(x) = -0.6954
x = -1.3818, f(x) = -0.9822, interpolacja f(x) = -0.9820
Python (dodatek)
# Interpolacja wielomianowa
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0074
#---------------------------

from math import sin, pi
from random import uniform

# STAŁE
#------

# Przedział interpolacji
# od XS do XE
XS = -pi / 2
XE =  pi / 2
# liczba węzłów
NN = 6
# Liczba interpolowanych
# punktów
N = 10
# Dokładność przyrównania do 0
EPS = 0.00001

# Tablice
#--------

# Współrzędne x węzłów
nx = []
# Współrzędne y węzłów
ny = []
# Współczynniki wielomianu
a = [0.0 for _ in range(NN)]

# Funkcje
#--------

# Funkcja interpolowana
#----------------------
def f(x):
    return sin(x)

# Funkcja wyznacza węzły
# dla wielomianu
#-----------------------
def set_nodes():
    # Dwa pierwsze węzły
    # definiują przedział
    # interpolacji
    nx.append(XS)
    nx.append(XE)
    # Pozostałe punkty są
    # rozsiane po przedziale

    # Minimalna odległość w osi OX
    # pomiędzy węzłami
    dx = (XE - XS) / 2 / NN
    while(len(nx) < NN):
        xx = uniform(XS,XE)
        t = False
        for j in nx:
            # Sprawdzamy odległość
            # węzłów w osi OX
            if abs(xx - j) < dx:
                t = True
                break
        # Węzeł za blisko innego,
        # losujemy go ponownie
        if t: continue
        nx.append(xx)

    # Dla współrzędnych x
    # liczymy współrzędne y
    for i in nx:
        ny.append(f(i))

# Oblicza wartość wielomianu
#---------------------------
def f_i(x):
    # Współczynnik przy x^(NN-1)
    w = a[NN - 1]
    for i in range(NN - 1, 0,-1):
        w *= x
        w += a[i-1]
    return w

# Funkcja rozwiązuje układ równań
# metodą eliminacji Gaussa
# NN - stopień macierzy
# AB - macierz rozszerzona NN x (NN + 1)
# X  - macierz rozwiązań NN x 1
# Jeśli układ został rozwiązany,
# to wynikiem jest true.
# Inaczej wynikiem jest false.
#--------------------------------
def gaussCrout(ab,x):
    # Przygotowujemy wektor
    # kolumnowy
    wk = [_ for _ in range(NN + 1)]

    # Eliminacja
    for i in range(NN - 1):
        k = i
        for j in range(i + 1, NN):
            if (abs(ab[i][wk[k]]) <
                abs(ab[i][wk[j]])):
                k = j

        if k != i:
            wk[k],wk[i] = wk[i],wk[k]

        if abs(ab[i][wk[i]]) < EPS:
            return False

        for j in range(i + 1, NN):
            m = (-ab[j][wk[i]] /
                  ab[i][wk[i]])
            for k in range(i + 1, NN + 1):
                ab[j][wk[k]] += (m *
                ab[i][wk[k]])

    # Obliczanie wartości niewiadomych
    for i in reversed(range(NN)):
        if abs(ab[i][wk[i]]) < EPS:
            return False
        s = ab[i][NN]
        for j in reversed(range(i + 1, NN)):
            s -= ab[i][wk[j]] * x[wk[j]]
        x[wk[i]] = s / ab[i][wk[i]]

    return True

# Program główny
#---------------

# Generujemy węzły
set_nodes()

# Tworzymy macierz
# układu równań
ab = [[0.0 for _ in range(NN + 1)]
           for _ in range(NN)]

for i in range(NN):
    # W ostatniej kolumnie
    # są współrzędne y
    ab[i][NN] = ny[i]
    # W pierwszej są 1
    ab[i][0] = 1
    # Pozostałe są potęgami
    for j in range(1, NN):
        ab[i][j] = nx[i] * ab[i][j-1]

# Wyznaczamy współczynniki
# wielomianu
if gaussCrout(ab,a):
    print("Interpolacja wielomianowa\n"
          "-------------------------\n")

    # Generujemy N punktów x
    # i liczymy dla nich y
    # przy pomocy wielomianu
    # i wyświetlamy wynik
    for i in range(N):
        x = uniform(XS, XE)
        y = f_i(x)
        print(f"x = {x:7.4f}"
              f", f(x) = {f(x):7.4f}"
              f", interpolacja f(x) = "
              f"{y:7.4f}")
else:
    print("Błąd w trakcie obliczeń!!!")
print()
input("Naciśnij Enter...")

Dla funkcji y = sin(x) otrzymaliśmy bardzo ładne wyniki, ale z innymi funkcjami tak pięknie być nie musi. Interpolacja wielomianowa jest niestabilna numerycznie i przy większej liczbie węzłów może pojawić się efekt Rungego. Istnieją lepsze metody interpolacji i kilka z nich przedstawimy w następnych rozdziałach.


do podrozdziału  do strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2026 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.