Metoda połowienia dla bystrzaków

Zadanie zostało opisane tak:

  1. Mamy funkcję f(x) ciągłą, monotoniczną i taką, że dla zadanych dwu punktów a i b f(a)f(b)<0 (funkcja zmienia znak w tym przedziale, a więc ma tam miejsce zerowe).
  2. Mamy znaleźć jej miejsce zerowe.
  3. Nie potrafimy analitycznie rozwiązać równanie f(x)=0.

Proponuję iteracyjną metodę (czyli krokową) szukania punktu najbliższego rozwiązaniu tego równania metodą połowienia.

  1. Znajdźmy środek przedziału (a,b): c=(a+b)/2.
  2. Jeżeli funkcja zmienia znak w przedziale (a,c) — odrzucamy przedział (c,b), w przeciwnym razie1 odrzucamy przedział (a,c).
  3. Powyższą procedurę powtarzamy tak długo, aż długość przedziału (a,b) będzie krótsza niż zadana wartość ε, lub gdy wartość funkcji f(c) będzie bliska zeru (|f(c)|<δ).

Dalsze założenia

Do dalszych obliczeń wybierzemy funkcję, której miejsca zerowego będziemy szukali. Będzie to funkcja sin(x). Z formalnego punktu widzenia nie spełnia ona założeń (jest okresowa), ale jeżeli ograniczymy rozważania do przedziału (2,5) wszystko powinno być dobrze. Oprócz tego znamy dokładną wartość miejsca zerowego: jest nią π.

Kompletny program może wyglądać tak:

int main(int argc, char **argv)
{
    double A, B, C;
    A = 2.;
    B = 5.;
    delta = 0.00001;
    epsilon = 0.0001;

delta to stała definiująca dostateczną „bliskość” f(c) od zera pozwalająca przerwać obliczenia, a epislon to stałą określająca graniczną długość przedziału poszukiwań.

pocz:
    C = ( A + B ) / 2.;
    if ( fabs( sin(C) ) < delta ) // czy wartość funkcji blisko zera?
    {
        printf("C=%.16f\\n", C);
        return 0;
    }
    else if ( sin(A) * sin(C) > 0 )
        A = C;
    else
        B = C;
    if ( fabs(A - B) > epsilon ) // czy przedział nie jest wystarczająco krótki?
        goto pocz;
    printf("!C=%.16f\\n", ( A + B ) / 2.);
    return 0;
}

Właściwie nie ma o czym więcej pisać.


  1. Teoretycznie możliwe jest, że f(c) będzie równe zero; warto to uwzględnić, ale szanse są niewielkie. ↩︎

Poprzedni
Następny