poniedziałek, 16 stycznia 2017

Obliczanie liczby Pi metodę Monte Carlo

"Metoda Monte Carlo (MC) jest stosowana do modelowania matematycznego procesów zbyt złożonych, aby można było przewidzieć ich wyniki za pomocą podejścia analitycznego.
Istotną rolę w metodzie MC odgrywa losowanie (
wybór przypadkowy) wielkości charakteryzujących proces, przy czym losowanie dokonywane jest zgodnie z rozkładem, który musi być znany."

Oryginalny przykład w Wikipedii jest napisany w C++:

#include <iostream>
#include <cmath>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
     srand(time(NULL)); //zainicjalizowanie maszyny generujacej liczby pseudolosowe
     int n;
     int nk = 0;
     double x,y;
     float s;
     
     cout << "Podaj liczbe losowanych pkt:" << endl;
     cin >> n;

     for(int i = 1; i <= n; i++)
     {           
         x = ((double)rand() / (RAND_MAX)) * 2 - 1;
         y = ((double)rand() / (RAND_MAX)) * 2 - 1;
         if(x*x + y*y <= 1)
         {
             nk++;
         }
     }
     
     cout << "Liczba pkt. w kole wynosi: " << nk << endl;
     cout << "Liczba pkt. w kwadracie wynosi: " << n << endl;
     s = 4. * nk / n;
     cout << "Liczba pi wynosi: " << s;
}

Jednak przerobienie go na Processing trwa chwilę i gdyby nie dodanie wyświetlania to kod byłby nawet prostszy.

Kilka elementów jest tu warte uwagi.
  1. Po pierwsze zniknęła główna pętla, bo została zastąpiona przez niejawną pętle wywołań funkcji draw() (od linii 24). Liczbę wywołań tej funkcji życzymy sobie mieć jak największą, zatem w funkcji setup() deklarujemy frameRate na 10000. To jednak oczywiście tylko "pobożne życzenie" - nie sądzę żeby udało się to na komputerze jaki macie do dyspozycji
  2. Ponadto znacznie uprościły się instrukcje losowania x i y (linie 26 i 27)Sam język daje nam do dyspozycji funkcję random(float), która może zwracać liczbę w zakresie 0..1, co w C++ uzyskiwaliśmy przez dzielenie rand()/RAND_MAX;
  3. A obliczenia wartości Pi wykonujemy nie na końcu programu, lecz za każdym razem gdy operator naciśnie jakiś klawisz na klawiaturze. Służy temu funkcja obsługi zdarzenia keyPressed(). Wyniki jej działania pojawiają się nie w oknie programu lecz na konsoli tekstowej. W szczególności dzięki predefiniowanej w Processingu zmiennej frameRate możemy dowiedzieć się ile naprawdę punktów na sekundę oblicza nasz program.
  4. Wreszcie korzystając z tego, że w przeciwieństwie do C++ mamy od razu do dyspozycji okno graficzne rysujemy też wylosowane punkty, a właściwie 1/4 z nich - te które mają obie współrzędne dodatnie. Dzięki użyciu liczby losowej w kolorach cały czas będzie widać postęp działania programu, także wtedy, gdy już każdy punkt okna zostanie co najmniej raz zamalowany.

Uruchomienie tego programu zajmie wam chwilę. Niestety uzyskanie sensownego przybliżenia liczby Pi będzie trwało znacznie dłużej. Ten program nie grzeszy szybkością. Głównym ograniczeniem jest liczba wywołań draw(). Na moim, dosyć starym komputerze nie udaje uzyskać więcej niż ok. 1500 wywołań draw() w ciągu sekundy.
Ale każde wywołanie draw() wiąże się z obsługą ekranu - co jest czasochłonne.
To daje nam furtkę do znaczącego przyśpieszenia obliczeń w tym programie.
SAMI WYMYŚLCIE JAK! ROZWIĄZANIE W NASTĘPNYM POŚCIE
POWODZENIA!

1 komentarz:

  1. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

    OdpowiedzUsuń