Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz  

obrazek

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

obrazek

Aproksymacja

Aproksymacje średniokwadratowe

SPIS TREŚCI REMANENT
Podrozdziały
 

Aproksymacja a interpolacja

W matematyce termin aproksymacja (ang. approximation) oznacza coś przybliżonego, co zwykle posiada prostszą postać i rozwiązuje pewien problem z pewnym przybliżeniem, ale za to prościej. Interpolacja natomiast oznacza szukanie wartości pośrednich, pomiędzy wartościami dokładnie znanymi (tzw. węzłami interpolacji). W interpolacji funkcja interpolująca przechodzi dokładnie przez węzły interpolacji. W aproksymacji staramy się dopasować funkcję aproksymującą tak, aby przechodziła możliwie najbliżej węzłów aproksymacji - ale nie musi przechodzić przez nie dokładnie. Np. mamy trzy punkty i chcemy dobrać taką prostą, aby przechodziła jak najbliżej tych punktów:

Innym przykładem jest sytuacja, gdy mamy serię pomiarów i chcemy dopasować do nich pewną znaną funkcję tak, aby błędy tego dopasowania były jak najmniejsze.


do podrozdziału  do strony 

Liniowa aproksymacja średniokwadratowa

Liniowa aproksymacja średniokwadratowa (ang. least-squares linear approximation) polega na dopasowaniu funkcji liniowej \({f(x) = ax + b}\) do zbioru  \(n\) punktów pomiarowych \((x_0,y_0)\), \((x_1,y_1)\), ..., \({(x_{n - 1},y_{n - 1})}\) tak, aby suma kwadratów odchyleń punktów od wartości tej funkcji liniowej była jak najmniejsza. Tworzymy funkcję kryterialną:

\( {S(a,b) = \sum\limits_{i=0}^{n-1} [y_i - (ax_i + b)]^2} \)
\(n\) liczba wszystkich punktów pomiarowych
\({(x_i,y_i)}\) współrzędne i-tego punktu pomiarowego
\({ax_i + b}\) wartość aproksymowana \(f(x_i)\)
\({y_i - (ax_i + b)}\) odchyłka wartości \(f(x_i)\) od wartości pomiarowej \(y_i\)

W celu znalezienia minimum funkcji \(S(a,b)\) wyznaczamy jej pochodne cząstkowe względem \(a\) i \(b\) i przyrównujemy je do zera:

\[
\dfrac{\partial S}{\partial a} = 0 \\ \\
\dfrac{\partial S}{\partial b} = 0
\]

Pochodne cząstkowe obliczamy:

\[
\begin{aligned}
&\dfrac{\partial S}{\partial a} = \sum\limits_{i=0}^{n-1} \{2 \cdot [y_i - (ax_i + b)] \cdot (-x_i)\} &= 0 \\
&\dfrac{\partial S}{\partial b} = \sum\limits_{i=0}^{n-1} \{2 \cdot [y_i - (ax_i + b)] \cdot (-1)\} &= 0
\end{aligned}
\]

Dzielimy oba równania obustronnie przez \((-2)\):

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} \{ x_i [y_i - (ax_i + b)]\} &= 0 \\
&\sum\limits_{i=0}^{n-1} [y_i - (ax_i + b)] &= 0
\end{aligned}
\]

Wyrażenia wewnątrz sum wymnażamy:

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} (x_i y_i - ax_i^2 - bx_i) &= 0 \\
&\sum\limits_{i=0}^{n-1} (y_i - ax_i - b) &= 0
\end{aligned}
\]

Rozbijamy na pojedyncze sumy i wyciągamy stałe \(a\) oraz \(b\) przed znak sumowania:

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} x_i y_i - a \sum\limits_{i=0}^{n-1} x_i^2 - b \sum\limits_{i=0}^{n-1} x_i &= 0 \\ \\
&\sum\limits_{i=0}^{n-1} y_i - a \sum\limits_{i=0}^{n-1} x_i - b \cdot n &= 0
\end{aligned}
\]

Przenosimy niewiadome na lewą stronę, a wyrazy z \(y_i\) na prawą stronę, tworząc ostateczny układ równań normalnych dla prostej:

\[
\begin{aligned}
\begin{cases}
&a \sum\limits_{i=0}^{n-1} x_i^2 + b \sum\limits_{i=0}^{n-1} x_i &= \sum\limits_{i=0}^{n-1} x_i y_i \\
&a \sum\limits_{i=0}^{n-1} x_i + b \cdot n &= \sum\limits_{i=0}^{n-1} y_i
\end{cases}
\end{aligned}
\]

Układ rozwiązujemy względem \(a\) i \(b\), otrzymując:

\[
\begin{aligned}
&a = \dfrac{n \sum\limits_{i=0}^{n-1} x_i y_i - \sum\limits_{i=0}^{n-1} x_i \sum\limits_{i=0}^{n-1} y_i}{n \sum\limits_{i=0}^{n-1} x_i^2 - \left(\sum\limits_{i=0}^{n-1} x_i\right)^2} \\ \\
&b = \dfrac{\sum\limits_{i=0}^{n-1} y_i  - a \sum\limits_{i=0}^{n-1} x_i}{n}
\end{aligned}
\]

We wzorach występują sumy o tych samych zakresach. Zastąpmy je symbolami:

\[
\begin{aligned}
&S_x = \sum\limits_{i=0}^{n-1} x_i \\
&S_y = \sum\limits_{i=0}^{n-1} y_i \\
&S_{xy} = \sum\limits_{i=0}^{n-1} x_i y_i \\
&S_{x2} = \sum\limits_{i=0}^{n-1} x_i^2
\end{aligned}
\]

Przed obliczeniem współczynnika \(a\) należy sprawdzić, czy mianownik ułamka jest równy zero. W takim przypadku obliczenia muszą być przerwane, bo oznacza to, iż wszystkie punkty danych wejściowych leżą na prostej pionowej, a takiej nie da się zapisać równaniem \({y = ax + b}\).

\[
\begin{aligned}
&M = n S_{x2} - S_x^2 \\ \\
&\text{Jeżeli } M = 0 \longrightarrow \text{BŁĄD: Punkty w pionie} \\ \\
&a = \dfrac{n S_{xy} - S_x S_y}{M} \\
&b = \dfrac{S_y - aS_x}{n}
\end{aligned}
\]

Otrzymujemy współczynniki prostej, która aproksymuje dane wejściowe linią prostą. Prosta ta pozwala wyznaczać punkty leżące poza zbiorem danych, co umożliwia nam aproksymacja. Przy interpolacji mogliśmy wyznaczać jedynie punkty pomiędzy danymi (słowo "inter" po łacinie znaczy "między", "pomiędzy"). Musisz jednak pamiętać, iż wyznaczona prosta aproksymacyjna jedynie przybliża prostą bazową, a błąd przybliżenia rośnie wraz z oddalaniem się od zbioru danych wejściowych.

Przykładowa implementacja

W celu przetestowania algorytmu aproksymacji średniokwadratowej poniższy program wykonuje następujące operacje:

Wszystko jest opisane w komentarzach.

C++
// Liniowa aproksymacja
// średniokwadratowa
// (C)2026 mgr Jerzy Wałaszek
// Metody numeryczne 0078
//---------------------------

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

using namespace std;

// Definicje stałych
//------------------

// Liczba danych
const int N = 100;
// Przedział dla danych
const double Xmin = -10;
const double Xmax =  10;
// Współczynniki prostej bazowej
const double aa =  1;
const double bb = -1;
// Przyrównanie do zera
const double EPS = 1e-12;

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

// Współrzędne danych
double x[N];
double y[N];

// Zwraca wartość pseudolosową
// w przedziale [-1;1]
//----------------------------
double random()
{
  return -1.0 + 2.0 * (double)rand() /
         RAND_MAX;
}

// Wyznacza losowe dane
//----------------------
void set_nodes()
{
  int i;
  for(i = 0; i < N; i++)
  {
    x[i] = Xmin + (Xmax - Xmin) *
           (double) rand() / RAND_MAX;
    y[i] = aa * x[i] + bb + random();
  }
}

// Program główny
//---------------
int main()
{
  int i;
  double a,b,Sx,Sy,Sxy,Sx2,M;

  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  cout << setprecision(4)
       << fixed;

  cout << "Liniowa aproksymacja "
          "średniokwadratowa\n"
          "---------------------"
          "-----------------\n\n";

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

  // Generujemy dane
  set_nodes();

  // Obliczamy współczynniki
  // prostej aproksymującej
  Sx = Sy = Sxy = Sx2 = 0;
  for(i = 0; i < N; i++)
  {
    Sx  += x[i];
    Sy  += y[i];
    Sxy += x[i] * y[i];
    Sx2 += x[i] * x[i];
  }

  // Liczymy mianownik
  M = N * Sx2 - Sx * Sx;

  // Testujemy mianownik
  if(fabs(M) < EPS)
    cout << "Błąd, dane w pionie";
  else
  {
    // liczymy a i b
    a = (N * Sxy - Sx * Sy) / M;
    b = (Sy - a * Sx) / N;

    // Wyświetlamy wynik porównawczy
    cout << "prosta bazowa       : "
            "y = " << aa
         << " * x + " << bb
         << endl << endl
         << "prosta aproksymowana: "
            "y = " << a
         << " * x + " << b
         << endl;
  }

  cout << endl;
  system("pause");
  return 0;
}
Wynik:
Liniowa aproksymacja średniokwadratowa
--------------------------------------

prosta bazowa       : y = 1.0000 * x + -1.0000

prosta aproksymowana: y = 1.0010 * x + -1.0218

Zauważ, iż po wyliczeniu z chmury danych prostej aproksymowanej \({y = ax + b}\) możemy przy jej pomocy wyliczać wartości leżące poza przedziałem \([Xmin,Xmax]\). To jest podstawowa różnica w stosunku do interpolacji. Im dalej od tego przedziału tym bardziej rośnie błąd aproksymacji.

Python (dodatek)
# Liniowa aproksymacja
# średniokwadratowa
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0078
#---------------------------

from random import uniform

# Definicje stałych
#------------------

# Liczba danych
n = 100
# Przedział dla danych
xmin = -10
xmax =  10
# Współczynniki prostej bazowej
aa =  1.0
bb = -1.0
# Przyrównanie do zera
eps = 1e-12

# Tablice
#--------

# Współrzędne danych
x = [0.0] * n
y = [0.0] * n

# Wyznacza losowe dane
#----------------------
def set_nodes():
    for i in range(n):
        x[i] = uniform(xmin, xmax)
        y[i] = (aa * x[i] + bb
               + uniform(-1,1))

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

print("Liniowa aproksymacja "
      "średniokwadratowa\n"
      "---------------------"
      "-----------------\n")

# Generujemy dane
set_nodes()

# Obliczamy współczynniki
# prostej aproksymującej
sx, sy, sxy, sx2 = 0, 0, 0, 0
for i in range(n):
    sx  += x[i]
    sy  += y[i]
    sxy += x[i] * y[i]
    sx2 += x[i] * x[i]

# Liczymy mianownik
m = n * sx2 - sx * sx

# Testujemy mianownik
if abs(m) < eps:
    print("Błąd, dane w pionie")
else:
    # liczymy a i b
    a = (n * sxy - sx * sy) / m
    b = (sy - a * sx) / n

    # Wyświetlamy wynik porównawczy
    print(f"prosta bazowa       : "
          f"y = {aa:.4f}"
          f" * x + {bb:.4f}\n"
          f"prosta aproksymowana: "
          f"y = {a:.4f}"
          f" * x + {b:.4f}")

print()
input("Naciśnij ENTER...")

do podrozdziału  do strony 

Paraboliczna aproksymacja średniokwadratowa

Gdy dane wejściowe nie są rozłożone wzdłuż prostej, lecz raczej np. po łuku, wtedy należy użyć aproksymacji parabolicznej, która dopasuje do nich parabolę \({f(x) = ax^2 + bx + c}\) w podobny sposób, jak poprzednio została dopasowana funkcja liniowa \({f(x) = ax + b}\).

Na wejściu mamy zbiór danych w postaci punktów \({(x_i,y_i), i = 0,1, \dots , n - 1}\), do których będziemy dopasowywać odpowiednią parabolę. Definiujemy funkcję kryterialną, która jest sumą kwadratów pionowych odchyłek wyliczanej paraboli od punktów danych:

\( {S(a,b,c) = \sum\limits_{i=0}^{n-1} [y_i - (ax_i^2 + bx_i + c)]^2} \)
\(n\) liczba wszystkich punktów pomiarowych
\({(x_i,y_i)}\) współrzędne i-tego punktu pomiarowego
\({ax_i^2 + bx_i + c}\) wartość aproksymowana \(f(x_i)\)
\({y_i - (ax_i^2 + bx_i + c)}\) odchyłka wartości \(f(x_i)\) od wartości pomiarowej \(y_i\)

Szukamy minimum funkcji \({S(a,b,c)}\). Warunkiem koniecznym istnienia minimum jest zerowa wartość pochodnych cząstkowych tej funkcji względem zmiennych \(a\), \(b\) i \(c\).

\[
\begin{aligned}
\dfrac{\partial S}{\partial a} &= 0 \\ \\
\dfrac{\partial S}{\partial b} &= 0 \\ \\
\dfrac{\partial S}{\partial c} &= 0
\end{aligned}
\]

Obliczamy pochodne cząstkowe, pamiętając o regule różniczkowania funkcji złożonej. Wnętrza nawiasów różniczkujemy odpowiednio względem \(a\): co daje \(-x_{i}^{2}\), względem \(b\): (\(-x_{i}\)) oraz względem \(c\): (\(-1\)):

\[
\begin{aligned}
&\dfrac{\partial S}{\partial a} = \sum\limits_{i=0}^{n-1} 2 \cdot [y_i - (ax_i^2 + bx_i + c)] \cdot (-x_i^2) = 0 \\
&\dfrac{\partial S}{\partial b} = \sum\limits_{i=0}^{n-1} 2 \cdot [y_i - (ax_i^2 + bx_i + c)] \cdot (-x_i) = 0 \\
&\dfrac{\partial S}{\partial c} = \sum\limits_{i=0}^{n-1} 2 \cdot [y_i - (ax_i^2 + bx_i + c)] \cdot (-1) = 0
\end{aligned}
\]

Wszystkie trzy równania dzielimy obustronnie przez \(-2\), aby pozbyć się stałych czynników przed sumami:

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} x_i^2 [y_i - (ax_i^2 + bx_i + c)] = 0 \\
&\sum\limits_{i=0}^{n-1} x_i [y_i - (ax_i^2 + bx_i + c)] = 0 \\
&\sum\limits_{i=0}^{n-1} [y_i - (ax_i^2 + bx_i + c)] = 0
\end{aligned}
\]

Następnie wymnażamy wyrażenia wewnątrz sum przez czynniki stojące przed nawiasami:

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} (x_i^2 y_i - ax_i^4 - bx_i^3 - cx_i^2) = 0 \\
&\sum\limits_{i=0}^{n-1} (x_i y_i - ax_i^3 - bx_i^2 - cx_i) = 0 \\
&\sum\limits_{i=0}^{n-1} (y_i - ax_i^2 - bx_i - c) = 0
\end{aligned}
\]

Korzystając z liniowości operacji sumowania, rozbijamy powyższe równania na pojedyncze sumy, wyprowadzając szukane stałe \(a\), \(b\) oraz \(c\) przed znaki sum.

\[
\begin{aligned}
&\sum\limits_{i=0}^{n-1} x_i^2 y_i - a \sum\limits_{i=0}^{n-1} x_i^4 - b \sum\limits_{i=0}^{n-1} x_i^3 - c \sum\limits_{i=0}^{n-1} x_i^2 = 0 \\
&\sum\limits_{i=0}^{n-1} x_i y_i - a \sum\limits_{i=0}^{n-1} x_i^3 - b \sum\limits_{i=0}^{n-1} x_i^2 - c \sum\limits_{i=0}^{n-1} x_i = 0 \\
&\sum\limits_{i=0}^{n-1} y_i - a \sum\limits_{i=0}^{n-1} x_i^2 - b \sum\limits_{i=0}^{n-1} x_i - c \cdot n = 0
\end{aligned}
\]

Porządkujemy układ, przenosząc wyrazy zawierające szukane niewiadome na lewą stronę, a sumy z wartościami \(y_i\) na prawą stronę. Otrzymujemy układ trzech równań liniowych z trzema niewiadomymi:

\[
\begin{cases}
&a \sum\limits_{i=0}^{n-1} x_i^4 + b \sum\limits_{i=0}^{n-1} x_i^3 + c \sum\limits_{i=0}^{n-1} x_i^2 &= \sum\limits_{i=0}^{n-1} x_i^2 y_i \\
&a \sum\limits_{i=0}^{n-1} x_i^3 + b \sum\limits_{i=0}^{n-1} x_i^2 + c \sum\limits_{i=0}^{n-1} x_i &= \sum\limits_{i=0}^{n-1} x_i y_i \\
&a \sum\limits_{i=0}^{n-1} x_i^2 + b \sum\limits_{i=0}^{n-1} x_i + c \cdot n &= \sum\limits_{i=0}^{n-1} y_i
\end{cases}
\]

W celu uproszczenia wprowadźmy symbole pomocnicze:

\[
\begin{aligned}
S_x &= \sum\limits_{i=0}^{n-1} x_i \\
S_y &= \sum\limits_{i=0}^{n-1} y_i \\
S_{x2} &= \sum\limits_{i=0}^{n-1} x_i^2 \\
S_{x3} &= \sum\limits_{i=0}^{n-1} x_i^3 \\
S_{x4} &= \sum\limits_{i=0}^{n-1} x_i^4 \\
S_{xy} &= \sum\limits_{i=0}^{n-1} x_i y_i \\
S_{x2y} &= \sum\limits_{i=0}^{n-1} x_i^2 y_i
\end{aligned}
\]

Zapiszmy układ równań z wykorzystaniem tych symboli pomocniczych:

\[
\begin{cases}
&a \cdot S_{x4} &+ \; b \cdot S_{x3} &+ \; c \cdot S_{x2} &= S_{x2y} \\
&a \cdot S_{x3} &+ \; b \cdot S_{x2} &+ \; c \cdot S_{x} &= S_{xy} \\
&a \cdot S_{x2} &+ \; b \cdot S_{x} &+ \; c \cdot n &= S_{y}
\end{cases}
\]

Rozwiązując powyższy układ równań liniowych jedną z opisanych wcześniej metod (np. Cramera, Gaussa, LU, itp.), wyznaczamy bezpośrednio poszukiwane współczynniki wielomianu aproksymującego: \(a\), \(b\) oraz \(c\).

Przykładowa implementacja

W celu przetestowania algorytmu parabolicznej aproksymacji średniokwadratowej poniższy program wykonuje następujące operacje:

Wszystko jest opisane w komentarzach.

C++
 
Wynik:
 
Python (dodatek)
 
 
do podrozdziału  do strony 

P4

xxx

do podrozdziału  do strony 

P5

xxx

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.