Wyszukiwanie Trójkowe to metoda, która znajduje maksimum (lub minimum) funkcji unimodalnej na pewnym przedziale [a, b]. Algorytm ten jest przykładem algorytmu "dziel i zwyciężaj". Jego zasada działania opiera się na pewnych prawdach matematycznych.
Szukając maksimum funkcji na przedziale [a, b] wiadomo, że istnieje taki x, że f(a) ≤ f(x) ≤ f(b). Przydatny jest też fakt, że maksimum na pewno nie leży w pierwsze trzeciej części podanego przedziału, ani w ostatniej trzeciej części. Weźmy dowolne dwa punkty p1 oraz p2, które będą leżały na przedziale [a, b] oraz p1 < p2. Istnieją wtedy dokładnie trzy możliwości:
Punkty p1 oraz p2 mogą być dowolne, ale optymalnie jest wybrać je tak, żeby równały się p1 = a + (b - a)/3 oraz p2 = b - (b - a)/3. Jest to związane z faktem, że przedział [a, b] dzielimy na trzy części, a wybrane punkty wskazują na punkty podziału przedziału.
Weźmy przykładowo funkcję f(x) = -2x2 - x + 2. Będziemy szukać jej miejsca zerowego na przedziale [-3, 3]. Jak widać na zamieszczonym po prawej stronie obrazku maksimum powinno znajdować się w punkcie xmax = 0.25. Poniżej zostały przedstawione kolejne iteracje wyliczania wyniku. Przyjmujemy, że wykonujemy, aż przedział będzie mniejszy niż 0.1. Wartości zostały przedstawione z dokładnością do czterech miejsc po przecinku.
Przedział [a, b] | f(p1) | f(p2) | Komentarz |
---|---|---|---|
[-3, 3] | 1 | -1 | odrzucamy prawy |
[-3, 1] | -1.88889 | 2.11111 | odrzucamy lewy |
[-1.66667, 1] | 1.5679 | 1.8642 | odrzucamy lewy |
[-0.777778, 1] | 2.1166 | 1.26063 | odrzucamy prawy |
[-0.777778, 0.407407] | 2.08977 | 1.98735 | odrzucamy prawy |
[-0.777778, 0.0123457] | 1.98518 | 2.125 | odrzucamy lewy |
[-0.514403, 0.0123457] | 2.10922 | 2.10994 | odrzucamy lewy |
[-0.33882, 0.0123457] | 2.12341 | 2.08278 | odrzucamy prawy |
[-0.33882, -0.10471] | 2.12477 | 2.11595 | odrzucamy prawy |
[-0.33882, -0.182747] | 2.12229 | 2.12454 | odrzucamy lewy |
[-0.286796, -0.182747] | 2.12499 | 2.12288 | odrzucamy prawy |
[-0.286796, -0.21743] | - | - | Różnica przedziałów mniejsza niż 0.1 |
W celu popełnienia jak najmniejszego błędu przyjmujemy, że punkt x leży pośrodku przedziału [a, b], więc x = -0.252113. Dokładny punkt to x = -0.25. Punkt ten można uzyskać zmniejszając precyzję poszukiwań na np. 0.0001. Wtedy algorytm zwraca x = -0.25001. Tym razem jednak zostało wykonane ponad 2 razy więcej iteracji.
Podczas implementacji tego algorytmu należy pamiętać, że znalezione wartości w programie będą miały postać przybliżoną, więc warto dodać dodatkowy argument precyzja, który określi dokładność z jaką mają być prowadzone obliczenia. Jednym z pomysłów jest precyzja określająca minimalną długość przedziału. Po osiągnięciu ustalonej długości przedziału podajemy wartość pośrodku przedziału, aby popełnić jak najmniejszy błąd.
Podczas implementowania zadania przyda się wczytać funkcję od użytkownika. Skorzystam tutaj z własnej biblioteki wielomian.h, która umożliwia wczytywanie wielomianu, jego wypisanie czy obliczenie wartości w konkretnym punkcie. Szerzej o bibliotece można znaleźć w artykule o Schemacie Hornera.
Na początek zastanówmy się nad przypadkiem, gdy chcemy wyszukać maksimum funkcji. Funkcja szukajMaksimum() przyjmuje cztery argumenty: wielomian w, który odpowiada funkcji, lewy i prawy jako przedział [a, b] oraz precyzja - do jakiej rozpiętości ma zostać zawężony maksymalnie przedział.
W analogiczny sposób implementuje się wyszukiwanie minimum z tą różnicą, że należy zamienić zawężanie przedziałów w instrukcji warunkowej. Wystarczy zmienić znak mniejszości na większości.
W celu sprawdzenia napisanej funkcji można skorzystać z poniższego fragmentu kodu:
Napisz rekurencyjną wersję kodu, która będzie wyszukiwać minimum funkcji modalnej używają Wyszukiwania Trójkowego. Przetestuj działanie napisanej funkcji.
Przykładowo dla funkcji f(x) = 7x2 - 4x - 3, przedziału [-3, 3] i precyzji Δ = 0.0001 program powinien znaleźć xmin = 0.285725.