Pole figury ograniczonej krzywymi

kategoria: Zadania z programowania

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 \(f(x)=\frac{-x^2}{50}\) od dołu
  • krzywą o równaniu \(g(x)=1+\frac{x^2}{100}-\frac{x}{200}\) od góry

Oblicz pole figury \(F(C)\) z dokładnością do \(0,01\).

wykres

Podejście matematyczne do problemu

Niestety w 4 technikum nie wiedziałem co to całka. Zanim zaprezentuję moje błędne 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ą \(X\) 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 \(X\).

Całka z \(f(x)\):

\[\int\limits_{0}^{10}\frac{-x^2}{50}dx=-\frac{1}{50}\int\limits_{0}^{10}x^2dx=-\frac{1}{50} \cdot \frac{x^3}{3}=\frac{-x^3}{150} \Bigg|_0^{10}=\\= -\frac{10^3}{150}-0=-\frac{1000}{150}=-\frac{20}{3}\]

Całka z g(x):

\[\int\limits_{0}^{10}1+\frac{x^2}{100}-\frac{x}{200}dx=\int\limits_{0}^{10}1 dx + \int\limits_{0}^{10}\frac{x^2}{100}dx-\int\limits_{0}^{10}\frac{x}{200}dx = \\ = x\Bigg|_0^{10}+\frac{1}{100} \cdot \frac{1}{3} \cdot x^3 \Bigg|_0^{10}-\frac{1}{200} \cdot \frac{1}{2} \cdot x^2 \Bigg|_0^{10}=10+\frac{1000}{300}-\frac{100}{400}= \\ = 10+\frac{10}{3}-\frac{1}{4}=\frac{157}{12}\]

Sumując wyniki otrzymujemy:

\[\frac{157}{12}+\frac{20}{3}=\frac{79}{4}=19,75\]

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.

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 0 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.

#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 = 0;

    for (float i=0; i<=10; i+=0.001)
    {
        calka += f2(i)-f1(i);
    }

    calka /=1000;

    cout << "Wynik calkowania: " << setprecision(4) << calka << endl;

    system("PAUSE");
    return 0;
}

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 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:

wykres2

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:

\(h=\frac{x_{k}-x_{p}}{n} h=\frac{10-0}{5}=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.

\(h=\frac{10-0}{1000}=0,01\)

Wynikiem całki (polem całkowitym) z górnej funkcji będzie suma wszystkich pól trapezów:

\(P_{c}=P_{1}+P_{2}+\ldots+P_{1000}\)

Rozpiszmy teraz wzór dla 1000 trapezów:

\(P_{c}=\frac{f(x_{0})+f(x_{1})}{2}\cdot h+\frac{f(x_{1})+f(x_{2})}{2}\cdot h+\ldots+\frac{f(x_{999})+f(x_{1000})}{2}\cdot h\)

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):

\(P_{c}=\frac{h}{2}\cdot(f(x_{0}) + 2\cdot f(x_{1})+\ldots+2\cdot f(x_{999})+f(x_{1000}))\)
\(P_{c}=h\cdot(\frac{f(x_{0})}{2}+f(x_{1})+\ldots+f(x_{999})+\frac{f(x_{1000})}{2})\)

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” \(x_{0}\) oraz \(x_{1000}\) 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:

\(Podst_{i}=f(x_{p} +i \cdot h)\), 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:

\(Podst_{0}=\frac{f(x_{p})}{2}\)
\(Podst_{1000}=\frac{f(x_{k})}{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 X.

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:

#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 = 0;
    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 = 0;

    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 0;
}

Ciekawe ile osób poradziło sobie z tym zadaniem mając na nie 50 minut?