Obliczanie pola figury ograniczonej krzywymi było jednym z zadań na maturze z informatyki w 2006 roku. Według mnie jest to najtrudniejsze zadania jakie zostało umieszczone w arkuszach egzaminacyjnych z informatyki. Gdy przygotowywałem się do matury i rozwiązywałem zadania z poprzednich lat, było to jedyne zadanie, którego nie udało mi się wtedy samemu rozwiązać. Treść zadania Przez F(C) oznaczamy figurę narysowaną w kartezjańskim układzie współrzędnych, która ograniczona jest przez: oś OY z lewej strony prostą o równaniu X=10 z prawej strony krzywą o równaniu od dołu krzywą o równaniu od góry Oblicz pole figury F(C) z dokładnością do 0,01. Podejście matematyczne do problemu Niestety w 4 technikum nie wiedziałem co to całka. Zanim zaprezentuję moje błędne Karol Trybulec p-programowanie.pl 1
myślenie oraz poprawne rozwiązanie, obliczmy zadania używając analizy matematycznej. Całka oznaczona z funkcji h(x) jest to pole obszaru ograniczonego wykresem funkcji h(x) oraz osią OX w określonych przedziałach [a,b]. Pole figury F(C) można prosto wyznaczyć całkując funkcje podane w zadaniu, w przedziałach [0,10]. Policzmy osobno dwie całki f(x) i g(x) oznaczone w przedziałach [0,10]. Licząc całkę z funkcji f(x) należy pamiętać aby zmienić znak, ponieważ jej wykres leży poniżej osi OX. Całka z f(x): Całka z g(x): Sumując wyniki otrzymujemy: Po krótkich obliczeniach, otrzymujemy dokładne pole figury F(C), które wynosi 19,75 [j^2]. Moje błędne rozwiązanie Będąc w technikum udało mi się po części dojść do prawidłowego wyniku, jednak nie mam pojęcia jak metoda została by oceniona przez egzaminatora. Przypuszczam, że miał bym wielkie szanse dostać 100% punktów, ponieważ pani Bożenka, która uczy informatyki w szkole średniej nie umiała by poprawnie ocenić tego zadania. Gdyby zobaczyła poprawny wynik i całą stronę kodu C++ nie zastanawiała by się nad szczegółami. Muszę także nadmienić, że na błędną drogę rozwiązania zadania nakierował mnie mój nauczyciel od programowania. Karol Trybulec p-programowanie.pl 2
Metoda polegała na podzieleniu obszaru figury na 10000 pasków. Deklarujemy obydwie funkcje, aby po podaniu argumentu otrzymać wartość. Ostatnim krokiem jest zbudowanie pętli for, zaczynając od i kończąc na 10. Najważniejszym elementem jest krok pętli, który wynosi 0,001. Jest to pierwsza rzecz, która jest niezgodna z treścią zadania, ponieważ pole miało zostać policzone z dokładnością do 0,01. Po kolejnych obiegach pętli sumujemy długość pasków w przedziale [0,10]. Przed wyświetleniem wyniku konieczne było zaokrąglenie wyniku do 2 miejsc po przecinku, ponieważ wynik nie zgadzał się o 0.0019. Po zmienieniu dokładności z 0,001 na 0,01 (czyli zgodnie z treścią), pole nie zgadzało się o około 0,1. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> #include <cstdlib> #include <iomanip> using namespace std; double f1(double x) return -x*x/50; double f2(double x) return 1+x*x/100-x/200; int main() float calka; calka = ; for (float i=; i<=10; i+=0.001) calka += f2(i)-f1(i); calka /=1000; cout << "Wynik calkowania: " << setprecision(4) << calka << endl; system("pause"); return ; Rozwiązanie optymalne Rozwiązaniem optymalnym było użycie metody trapezów do obliczania całek numerycznych. Nie wiem kto wpadł na chytry plan, aby umieścić takie zadanie na maturze w Karol Trybulec p-programowanie.pl 3
szkole średniej, jednak na moje oko było ono trudne, szczególnie narzucona w kluczu metoda trapezów której w szkole średniej chyba nikt nie przerabiał (MES). Przeglądając opinie na temat zadania przeczytałem o osobach, które policzyły pole figury matematycznie (całkami), jednak nikt nie pisał aby wstrzelił się w klucz. Całkowanie numeryczne metodą trapezów opisałem w osobnym artykule: Całkowanie numeryczne metoda trapezów, dlatego nie będę dokładnie jej opisywał. Dalsza część artykułu pisana jest z przekonaniem, że rozumiesz metodę trapezów. Rysunek poglądowy wykorzystując trapezy wygląda mniej więcej tak: Trapezami wypełniłem tylko jedną funkcję, aby rysunek wyglądał bardziej przejrzyście. Na rysunku znajduje się 5 trapezików, co oznacza że dokładność obliczeń (krok h) wynosi 2: W zadaniu jest napisane, aby pole figury obliczyć z dokładnością 0,01 co oznacza, że krok (h) musi wynosić dokładnie 0,01. Aby uzyskać taką dokładność należy podzielić obszar pod całką na 1000 malutkich trapezów, co staje się nie możliwe do przedstawienia graficznie. Wynikiem całki (polem całkowitym) z górnej funkcji będzie suma wszystkich pól trapezów: Karol Trybulec p-programowanie.pl 4
Rozpiszmy teraz wzór dla 1000 trapezów: Uprośćmy wzór, wyłączając odpowiednie czynniki przed nawias (jeżeli nie wiesz o co chodzi to przeczytaj artykuł o całkowaniu numerycznym): Podanym wzorem można obliczyć całkę z funkcji g(x). Taki sam wzór posłuży do liczenia całki z funkcji f(x), będzie trzeba tylko zmienić znak. Wzór poda nam pole z dokładnością do 0,01 ponieważ podzieliliśmy obszar na 1000 małych trapezów. Oczywiście nie będziemy liczyć tego na piechotę a wzór został wyprowadzony tylko po to, aby zobaczyć ważną zależność. Aby obliczyć całkę z g(x) (czyli pole) wystarczy zsumować podstawy wewnętrznych trapezów, następnie zsumować z nimi zewnętrzne podstawy x0 oraz x1000 podzielone przez 2. Na samym końcu otrzymaną liczbę mnożymy przez krok (h). Długości podstaw bardzo łatwo obliczyć w C++ używając pętli i iteratora: dla i=1..999 Po wyjściu z pętli liczymy długości podstaw brzegowych zgodnie z wyprowadzonym wzorem i dzielimy je przez 2: Na samym końcu sumujemy poszczególne podstawy i mnożymy przez krok h. Pamiętaj aby funkcję f(x) odejmować a nie dodawać, ponieważ jej wykres znajduje się poniżej osi OX. Kompletny kod w C++, spełniający wymagania egzaminatorów, liczący pole z dokładnością do 0,01 i wykorzystujący metodę trapezów wygląda następująco: Karol Trybulec p-programowanie.pl 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> #include <cstdlib> using namespace std; // funkcja do scalkowania double f(double x) return -x*x/50; double g(double x) return 1+x*x/100-x/200; int main() float xp, xk, h, calka; int n; // przedzialy xp = ; xk = 10; // im wieksze n tym wieksza dokladnoœc (np. n=1000) n = 1000; h = (xk - xp) / (float)n; cout << "krok (dokladnosc): h=" << h << endl; calka = ; for (int i=1; i<n; i++) // podstawy trapezów wewnetrzne calka += g(xp + i * h)-f(xp + i * h); // podstawy trapezów brzegowe calka += g(xp) / 2; calka += g(xk) / 2; calka -= f(xp) / 2; calka -= f(xk) / 2; // calosc mnożymy przez krok h calka *= h; cout << "Wynik calkowania: " << calka << endl; system("pause"); return ; Karol Trybulec p-programowanie.pl 6
Ciekawe ile osób poradziło sobie z tym zadaniem mając na nie 50 minut? Działający program znajdziesz przechodząc na podstronę: http://p-programowanie.pl/pliki/calka1.html Karol Trybulec p-programowanie.pl 7