MES (FEM) - Metoda elementów skończonych - Projekt C++ interpolacja liniowa
[b]### Treść zadania ###[/b] [cytat]Na ile podprzedziałów należy podzielić przedział 0 do 5 aby funkcja f(x)=1/(1+x^2) była interpolowana z dokładnością co najmniej 1e-5. Normę błędu liczyć jako L2. Dla uproszczenia dokonywać podziału na 1,2,4,8,16,...,2^n podprzedziałów. Interpolacja liniowa[/cytat] [b]UWAGA:[/b] Program kompilowany w Dev-C++ [code=cpp]#include #include #include #include #include using namespace std; //================================================================================= struct wspolrzedne { double x; double y; }; // Nasza funkcja double f(double x) { return 1/(1+x*x); } // Wzor funkcji liniowej przechodzącej przez dwa punkty [x0, f(x0)] i [x1, f(x1)] double p(double x, double x0, double x1) { return ( f(x0) + ( (f(x1)-f(x0))/(x1-x0) ) * (x-x0) ); }
[b]### Treść zadania ###[/b] [cytat]Na ile podprzedziałów należy podzielić przedział 0 do 5 aby funkcja f(x)=1/(1+x^2) była interpolowana z dokładnością co najmniej 1e-5.
Normę błędu liczyć jako L2.
Dla uproszczenia dokonywać podziału na 1,2,4,8,16,...,2^n podprzedziałów.
Interpolacja liniowa[/cytat]
[b]UWAGA:[/b] Program kompilowany w Dev-C++
[code=cpp]#include #include #include #include #include
using namespace std;
//=================================================================================
struct wspolrzedne { double x; double y; };
// Nasza funkcja double f(double x) { return 1/(1+x*x); }
// Wzor funkcji liniowej przechodzącej przez dwa punkty [x0, f(x0)] i [x1, f(x1)] double p(double x, double x0, double x1) { return ( f(x0) + ( (f(x1)-f(x0))/(x1-x0) ) * (x-x0) ); }
/* ### UWAGA: Funkcja pomocnicza. Nie jest wykorzystywana w programie.
Całkowanie numeryczne - metoda Simpsona
Metoda ma zastosowanie do funkcji stablicowanych w nieparzystej liczbie równo odległych punktów
(wliczając końce przedziału całkowania). Metoda opiera się na przybliżaniu funkcji całkowanej
przez interpolację wielomianem drugiego stopnia.
xp - x początkowe
xk - x końcowe
n - liczba punktów podziałowych dla przedziału od xp do xk
*/ double calka_simpsona_z_f(double xp, double xk, int n) { double x; double s = 0; double st = 0; double h = (xk - xp) / n; // odleglosc miedzy kolejnym wezlem
for(int i=1; i<=n; i++)
{
x = xp+i*h;
st += f(x-h/2);
if(i<n) {
s += f(x);
}
}
return h / 6 * (f(xp) + f(xk) + 2 * s + 4 * st);
}
/* ### UWAGA: Funkcja pomocnicza. Nie jest wykorzystywana w programie.
Całkowanie metodą Simpsona funkcji liniowej p przechodzącej przez dwa punkty x0 i x1
*/ double calka_simpsona_z_p(double xp, double xk, int n, double x0, double x1) { double x; double s = 0; double st = 0; double h = (xk - xp) / n; // odleglosc miedzy kolejnym wezlem
for(int i=1; i<=n; i++)
{
x = xp+i*h;
st += p(x-h/2, x0, x1);
if(i<n) {
s += p(x, x0, x1);
}
}
return h / 6 * (p(xp, x0, x1) + p(xk, x0, x1) + 2 * s + 4 * st);
}
/* Funkcja interpolująca. Jest złożona z wielu funkcji liniowych. x - argument dla ktorego liczymy wartosc tab - tablica wspolrzednych punktow wezlowych
*/ double finterp(double x, vector &tab) { double y = 0; // przechowuje obliczoną wartosc funkcji interpolujacej
for(int i=0; i<tab.size(); i++)
{
// szukamy między jakimi punktami węzłowymi znajduje się podany jako argument x
// a następnie obliczamy wartosc funkcji liniowej przechodzącej przez te punkty węzłowe dla podanego x
if(x <= tab[i+1].x)
{
y = p(x, tab[i].x, tab[i+1].x);
break; // po znalezieniu odpowiedniego przedziału przerywamy pętle
}
}
return y;
}
/* Funkcja g(x) = (f(x) - finterp(x))^2 Jest to funkcja pod całkowa do wzoru na norme euklidesową - L2 */ double g(double x, vector &tab) { double tmp = f(x)-finterp(x, tab); return pow(tmp, 2); }
/* Całka Simpsona z funkcji g Całka ta zostanie wykorzystana do liczenia normy L2
Zalecana liczba podprzedzialow n to iloczyn liczby podprzedziałów całej funkcji
i ilości podziałów w danym podprzedziale.
*/ double calka_simpsona_z_g(double xp, double xk, int n, vector &tab) { double x; double s = 0; double st = 0; double h = (xk - xp) / n; // odleglosc miedzy kolejnym wezlem
for(int i=1; i<=n; i++)
{
x = xp+i*h;
st += g(x-h/2, tab); // suma wartosci dla wczesniejszych węzłów
if(i<n) { // zabezpieczamy sie przed wyjsciem poza przedzial
s += g(x, tab); // suma wartosci dla następnych węzłów
}
}
return h / 6 * (g(xp, tab) + g(xk, tab) + 2 * s + 4 * st);
}
//=================================================================================
int main(int argc, char *argv[]) { cout«"### (c) Webook.pl (2010/2011) - Interpolacja liniowa ###nn"; //cout.setf(ios::showpoint); // uzupelnia liczby zerami po przecinku cout.precision(30); // wyswietla liczby zmienno przecinkowe do 30 miejsc po przecinku
double eps = pow(10.,-5); // wymagana dokładność
double a = 0.; // początek przedziału
double b = 5.; // koniec przedziału
int licznik = 2;
double n = pow(2., licznik); // liczba podprzedzialow od ktorej startujemy
double h; // odleglosc miedzy kolejnym wezlem na osi x
double L2; // zmienna przechowujaca norme euklidesowa
wspolrzedne tmp_wsp; // zmienna pomocnicza do tworzenia struktury zawierajacej wspolrzedne punktu [x,y]
vector<wspolrzedne> tab; // tworzymy vector złożony ze struktury dwóch zmiennych x i y
// powtarzamy pętlę aż zostanie wywołany break
while(1)
{
tab.clear(); // kasujemy stan vectora przy rozpoczeciu kazdej kolejnej iteracji
h = (b-a)/n; // liczymy odleglosc miedzy kolejnym wezlem na osi x
cout<<"=== Krok "<<licznik-1<<" ================n"
<<"n = "<<n<<" (liczba podprzedzialow)n"
<<"h = "<<h<<" (odleglosc miedzy kolejnym wezlem)nn";
cout<<"Wspolrzedne punktow wezlowych:n";
// mamy n+1 punktow węzłowych dlatego zaczynamy od 0
for(double i=0; i<=n; i++)
{
tmp_wsp.x = h*i;
tmp_wsp.y = f(h*i);
tab.push_back(tmp_wsp); // wstawiamy na koniec vectora nasze wspolrzedne
// podgląd wspolrzednych
cout<<"Punkt "<<i<<" # x: "<<tab[i].x<<", y: "<<tab[i].y<<"n";
}
// każdy podprzedział jest dzielony na kolejne 1000 podprzedzialow w czasie calkowania
L2 = sqrt(calka_simpsona_z_g(a, b, n*1000, tab));
cout<<"nNorma L2 = "<<L2<<"nnn";
if(L2 <= eps)
{
break; // przerywamy pętle gdy norma jest mniejsza lub rowna zadanej dokładności
}
else
{
n = pow(2., ++licznik); // zwiekszamy liczbe podprzedzialow
}
}
// po wyjściu z pętli wypisujemy odpowiedź.
cout<<"Wymagana liczba podprzedzialow to: "<<n;
/*
W ostatnim kroku zostanie wypisane:
Norma L2 = 9.4489457156375221e-006
Wymagana liczba podprzedzialow to: 512
*/
// testowe sprawdzenie jak działają funkcje:
//cout<<"g: "<<g(1, tab);
//cout<<"finterp: "<<finterp(1.25, tab);
cout<<"nn";
system("PAUSE");
return EXIT_SUCCESS;
} [/code]