Projekt jest graficzną aplikacją w C++ do numerycznego wyznaczania pierwiastków równań nieliniowych o postaci: f(x) = 0
Aplikacja korzysta z biblioteki Raylib do interfejsu użytkownika, z MPFR/GMP oraz lokalnej implementacji arytmetyki przedziałowej. Funkcja rozwiązywana przez program jest ładowana z biblioteki dynamicznej .so, dlatego można zmieniać równanie bez przebudowy głównej aplikacji.
Domyślna biblioteka funkcji:
./build/funcs/func.so
Program udostępnia trzy tryby wybierane przyciskami w oknie aplikacji:
| Tryb | Dane wejściowe | Opis |
|---|---|---|
Real numbers |
liczba rzeczywista | Metoda Newtona-Raphsona drugiego rzędu dla punktu startowego x0. |
Interval for real numbers |
liczba rzeczywista | Punkt x0 jest zamieniany na punktowy przedział i dalej liczony metodą przedziałową. |
Interval for interval numbers |
przedział | Metoda przedziałowa uruchamiana bezpośrednio dla zadanego przedziału. |
Pola Epsilon i Max iterations pozwalają zmienić tolerancję oraz limit iteracji. Domyślnie:
| Parametr | Wartość |
|---|---|
epsilon |
1e-15 |
MAX_ITER |
100 |
Okno aplikacji ma rozmiar 1400 x 700 i odświeża się z docelową szybkością 60 FPS.
Wymagane biblioteki systemowe:
raylib, mpfr, gmp, dl
Budowanie projektu:
makeUruchomienie z domyślną funkcją:
./build/mainUruchomienie z wybraną biblioteką funkcji:
./build/main ./build/funcs/func1.soMakefile buduje aplikację oraz przykładowe biblioteki:
| Plik źródłowy | Biblioteka | Funkcja |
|---|---|---|
funcs/func.cpp |
build/funcs/func.so |
f(x) = x^2 - 2 |
funcs/func1.cpp |
build/funcs/func1.so |
f(x) = x^3 - x - 2 |
funcs/func2.cpp |
build/funcs/func2.so |
f(x) = cos(x) - x |
funcs/func3.cpp |
build/funcs/func3.so |
f(x) = sin(x)^2 + 0.5sin(x) - 0.5 |
Program implementuje metodę Newtona-Raphsona drugiego rzędu. Dla punktu x_n używane jest rozwinięcie:
Po podstawieniu h = x - x_n otrzymujemy równanie kwadratowe:
Kolejne przybliżenie jest wyznaczane z kandydatów:
Dla liczb rzeczywistych program wybiera kandydata dającego mniejszy krok względem aktualnego punktu. Jeżeli f''(x) jest bliska zeru albo wyróżnik jest ujemny, obliczenia są przerywane z komunikatem błędu.
Dla przedziałów te same operacje są wykonywane na obiektach Interval<long double>. W każdej iteracji program:
- zapamiętuje poprzedni przedział,
- liczy środek przedziału,
- oblicza
f(m),f'(m)orazf''(X), - buduje dwa kandydaty drugiego rzędu,
- wybiera kandydata bliższego środkowi,
- sprawdza bezwzględny i względny warunek stopu.
Warunki stopu dla przedziałów sprawdzają zmianę obu końców przedziału:
|a_{k+1} - a_k| < epsilon
|b_{k+1} - b_k| < epsilon
oraz wersję względną z przeskalowaniem przez max(|x|, 1).
Deklaracje procedur obliczeniowych znajdują się w math/Newton.h, a implementacje w math/Newton.cpp.
void runNewtonRaphsonReal(
long double x0,
RealFn f,
RealFn df,
RealFn ddf,
OutputBox& out,
int MAX_ITER,
long double epsilon
);Uruchamia metodę punktową dla rzeczywistego punktu startowego x0.
void runNewtonRaphsonInterval(
Interval<long double> x0,
IntervalFn f,
IntervalFn df,
IntervalFn ddf,
OutputBox& out,
int MAX_ITER,
long double epsilon
);Uruchamia metodę przedziałową dla przedziału startowego x0.
Typy funkcji są zdefiniowane w common/Types.h:
using RealFn = std::function<long double(long double)>;
using IntervalFn = std::function<Interval<long double>(Interval<long double>)>;
typedef long double (*FnPtr)(long double);
typedef void (*IVFnPtr)(long double, long double, long double*, long double*);W trybie punktowym należy wpisać liczbę rzeczywistą:
1.5
W trybie przedziałowym należy wpisać przedział domknięty:
[1, 2]
Spacje w zapisie przedziału są ignorowane. Błędny format wejścia powoduje wyczyszczenie pola wynikowego i wyświetlenie:
Error: wrong input format
Wyniki są wypisywane w komponencie OutputBox.
Dla trybu Real numbers program pokazuje kolejne iteracje i znaleziony pierwiastek:
Iter 1: x=... step=...
Iter 2: x=... step=...
--- Roots: ---
Root: ...
Dla trybów przedziałowych program pokazuje kolejne przedziały, ich szerokość i krok:
Iter 1: [a, b] w=... step=...
Iter 2: [a, b] w=... step=...
--- Interval root: ---
[a, b] w=...
Po przekroczeniu limitu iteracji wypisywany jest aktualny przedział:
Max iterations reached.
Current interval: [a, b]
Biblioteka dynamiczna z funkcją musi eksportować sześć symboli C:
extern "C" {
long double f(long double x);
long double df(long double x);
long double ddf(long double x);
void f_iv(long double a, long double b, long double* ra, long double* rb);
void df_iv(long double a, long double b, long double* ra, long double* rb);
void ddf_iv(long double a, long double b, long double* ra, long double* rb);
}Znaczenie symboli:
| Symbol | Znaczenie |
|---|---|
f |
funkcja rzeczywista |
df |
pierwsza pochodna funkcji rzeczywistej |
ddf |
druga pochodna funkcji rzeczywistej |
f_iv |
funkcja przedziałowa |
df_iv |
pierwsza pochodna funkcji przedziałowej |
ddf_iv |
druga pochodna funkcji przedziałowej |
Funkcje przedziałowe przyjmują końce przedziału a, b, a wynik zwracają przez wskaźniki ra, rb.
Ładowanie biblioteki odbywa się w loadFunctions z math/IntervalUtils.cpp przez dlopen i dlsym. Jeżeli biblioteka podana w argumencie programu nie zostanie załadowana, aplikacja próbuje wrócić do ./build/funcs/func.so.
Dla domyślnej funkcji:
uruchom aplikację:
make
./build/main ./build/funcs/func.soWybierz Real numbers i wpisz:
1.5
Oczekiwany wynik to przybliżenie:
Root: 1.414213562373095...
Dla tej samej funkcji wybierz Interval for interval numbers i wpisz:
[1, 2]
Program powinien znaleźć przedział otaczający dodatni pierwiastek:
--- Interval root: ---
[1.41421356237309..., 1.41421356237310...] w=...
Dokładne końce przedziału mogą zależeć od zaokrągleń i kolejnych iteracji.