Strona główna » Algorytmy » Artykuły » Funkcja kwadratowa
 

Funkcja kwadratowa

Definicja

Funkcję kwadratową nazywamy funkcję zadaną wzorem f(x) = ax2 + b·x + c, gdzie a, b, c to współczynniki rzeczywiste. W funkcji kwadratowej a ≠ 0.

Implementacja

Wyróżnik trójmianu

Wyróżnik funkcji kwadratowej dany jest wzorem Δ = b2 - 4ac. Po jego wyliczeniu możliwe jest wyliczenie pierwiastków równania kwadratowego. Problem ten rozwiązuje poniższy kod:

  1. double wyroznik(double a, double b, double c){
  2.   return b * b - 4 * a * c;
  3. }

Niestabilne obliczanie pierwiastków

Napiszemy teraz funkcję, która na podstawie wyróżnika wypisze na ekran rozwiązania lub komunikat o braku rozwiązań:

  1. void miejsca_zerowe_niestabilne(double a, double b, double c){
  2.   double delta = wyroznik(a, b, c);
  3.   if(delta < 0){
  4.     cout << "Funkcja nie ma miejsc zerowych" << endl;
  5.   } else if(delta == 0){
  6.     cout << "x = " << (-b)/(2*a) << endl;
  7.   } else {
  8.     cout << "x1 = " << (-b - sqrt(delta))/(2*a) << endl;
  9.     cout << "x2 = " << (-b + sqrt(delta))/(2*a) << endl;
  10.   }
  11. }

2.) Wyliczamy wyróżnik i zapisujemy go do zmiennej delta. (3.) Jeśli delta < 0 to (4.) wypisujemy, że brak rozwiązań. Jednak jeśli nie to to (5.) sprawdzamy czy delta = 0. Jeśli tak to (6.) Obliczamy jedyny, podwójny pierwiastek funkcji kwadratowej. (7.) Jednak, gdy oba warunki są nieprawdziwe to delta > 0, więc (8., 9.) wyliczamy obydwa pierwiastki x1, x2.

Takie rozwiązanie jest niestabilne, a wszystko z powodu błędów zaokrągleń komputera. Mianowicie wyliczamy obydwa pierwiastki niezależnie od siebie. Błędy zaokrągleń mogą spowodować, że rozwiązania nie będą spełniać innych założeń funkcji kwadratowej - np. wzorów Viete'a.

Stabilne rozwiązanie

Za wzory Viete'a przyjmujemy następujące wzory: oraz

Wykorzystując wzór na iloczyn pierwiastków równania można uzyskać stabilne rozwiązanie. Pozwoli to nam zredukować błąd zaokrąglenia - błąd oszacowania pierwszego pierwiastka wpłynie na błąd drugiego. Można się zastanawiać czy wtedy chociaż jeden pierwiastek będzie "prawidłowy". W przypadku liczb całkowitych i niektórych liczb rzeczywistych jest to prawdziwe, ale nie zapisując wyniku w formie ułamków (z ewentualnymi pierwiastkami) to nigdy błędy przybliżenia się nie pozbędziemy.

Takie rozwiązanie wprowadza poprawki w linijkach (8., 9.):

  1.     double x1 = (-b - sqrt(delta))/(2*a);
  2.     cout << "x1 = " << x1 << "\nx2 = " << c/(a*x1) << endl;

(8.) Na początek wyliczamy jeden z pierwiastków i zapisujemy do zmiennej x1. (9.) Na ekran wypisujemy kolejno wartość wyliczonego pierwiastka, a wartość drugiego wyliczamy z przekształconego wzoru Viete'a: . Funkcję wyliczająca pierwiastki w ten sposób nazywamy stabilną.

Testowanie funkcji

Poniższa funkcja main() poprosi użytkownika o wprowadzenie punktu do osiągnięcia, a następnie wypisze na ekran czy punkt można osiągnąć i w ilu krokach.

  1. int main () {
  2.   double a, b, c;
  3.   cin >> a >> b >> c;
  4.   miejsca_zerowe(a,b,c);
  5.   system("pause");
  6.   return 0;
  7. }

Zadanie

Na wejściu otrzymamy ciąg znaków, który będzie zapisem liniowym funkcji kwadratowej. Na wyjście powinny zostać wypisane jego pierwiastki oraz postać kanoniczna. Przykładowo po wpisaniu f(x)=x^2+6x+9 program wypisze na ekran pierwiastki (lub pierwiastek lub brak) oraz postać kanoniczną.

  1. x = -3
  2. f(x) = (x+3)^2

Odczytywanie danych

Za punkt odniesienia będziemy przyjmować liczbę. Jeśli znak przed nią to minus "-" to będziemy wiedzieli, że dany współczynnik ma ujemną wartość. Który współczynnik odczytujemy stwierdzimy po tym czy po liczbie stoi x i jeśli tak to jaką ma potęgę. Zakładamy, że funkcja może mieć dowolną postać tj. współczynnik c może mieć postać np. 2 + 3.

Odczytywanie wyrażenia rozpoczniemy od wczytana wprowadzonej linijki tekstu:

  1. void read_data(){
  2.   char* txt = new char[128];
  3.   cin.getline(txt, 128);
  4.   int i = 0;
  5.   for(; txt[i-1]!='=';i++);

(2.) Deklarujemy bufor, aby przechować wprowadzone wyrażenie. (3.) Wczytujemy wyrażenie. (4.) Deklarujemy zmienną i, która będzie wskazywała, który znak teraz rozpatrujemy. (5.) Przesuwamy wskaźnik na znak po znaku "=".

  1.   double a = 0, b = 0, c = 0;
  2.   while(txt[i - 1]){
  3.     bool minus = (txt[i - 1] == '-');
  4.     double liczba;

(6.) Deklarujemy współczynniki. (7.) Rozpoczynamy pętle wczytującą składniki dopóki nie znajdziemy znaku \0. (8.) Sprawdzamy jaki znak ma składnik. (9.) Deklarujemy zmienną liczba do której będziemy wczytywać wartość współczynnika.

  1.     if(txt[i] >= '0' && txt[i] <= '9'){
  2.       liczba = 0;
  3.       while (txt[i] >= '0' && txt[i] <= '9' && txt[i]){
  4.         liczba *= 10;
  5.         liczba += txt[i] - '0';
  6.         i++;
  7.       }

(10.) Jeśli aktualnie wczytywany znak to cyfra to (11.) ustawiamy liczba na 0 i (12. - 16.) wczytujemy liczbę z tekstu.

  1.     } else {
  2.       liczba = 1;
  3.     }
  4.     if(minus) liczba*=-1;

(17.) Jeśli jednak wczytany znak to nie liczba to oznacza to, że jest to współczynniki przy x lub x^2, więc ustalamy liczba na 1. (20.) Jeśli wcześniej ustaliliśmy, że znak jest minus to mnożymy liczba razy -1.

  1.     if(txt[i] == 'x'){
  2.       i++;
  3.       if(txt[i] == '^'){
  4.         i+=2;
  5.         a += liczba;
  6.       } else {
  7.         b += liczba;
  8.       }
  9.     } else {
  10.       c += liczba;
  11.     }

W zależności od rozpoznanych znaków dalej w tekście odpowiednio przesuwamy indeks, zwiększamy odpowiedni współczynnik (a, b, c).

  1.     i++;
  2.   }
  3.   miejsca_zerowe_stabilne(a, b, c);
  4.   postac_kakoniczna(a, b, c);
  5. }

(32.) Na koniec pętli przesuwamy wskaźnik o 1 dalej. (34.) Wypisujemy miejsca zerowe oraz (35.) postać kanoniczną.

Postać kanoniczna

Do wypisywania postaci kanonicznej stworzymy funkcję pomocniczą postac_kakoniczna(), aby ułatwić wypisywanie współczynników. Dzięki niej możemy przekazać wartość do przekazania i czy powinien zostać wypisany znak plus "+" oraz czy wypisana powinna być liczba kiedy wynosi 1 (chcemy uniknąć wypisania 1 kiedy stoi przy x lub x2).

  1. void writeNumber(double a, bool plus_sign = true, bool value = false){
  2.   if(a > 0 && plus_sign) cout << "+";
  3.   if(a != 1 || value) cout << a;
  4. }

Wypisanie postaci kanonicznej to kwestia odpowiedniego wywoływania funkcji writeNumber() oraz używania cout <<.

  1. void postac_kakoniczna(double a, double b, double c){
  2.   double delta = wyroznik(a, b, c);
  3.   if(delta < 0){
  4.     cout << "f(x)=";
  5.     writeNumber(a, false, false);
  6.     cout << "x^2 ";
  7.     writeNumber(b);
  8.     cout << "x";
  9.     writeNumber(c);
  10.   } else if(delta == 0){
  11.     cout << "f(x)=";
  12.     writeNumber(a, false, false);
  13.     cout << "(x";
  14.     writeNumber(b/(2*a), true, true);
  15.     cout << ")^2";
  16.   } else {
  17.     double x1 = (-b - sqrt(delta))/(2*a);
  18.     cout << "f(x)=";
  19.     writeNumber(a, false, false);
  20.     cout << "(x";
  21.     writeNumber(-x1, true, true);
  22.     cout << ")(x";
  23.     writeNumber(-c/(a*x1), true, true);
  24.     cout << ")";
  25.   }
  26.   cout << endl;
  27. }

Testowanie funkcji

Wszystkie zadania, które zwykle znalazłyby się w funkcji main() realizuje funkcja read_data().

  1. int main () {
  2.   read_data();
  3.   system("pause");
  4.   return 0;
  5. }

Zadania

Zadanie 1

Dopisz funkcję wierzcholek(), która poda gdzie znajduje się wierzchołek funkcji i go wypisze na ekran.