Szacowanie wartości liczby \(\pi\) metodą Monte Carlo opiera się na geometrycznym prawdopodobieństwie trafienia losowego punktu w określony obszar. Proces ten wykorzystuje generowanie punktów z rozkładu jednostajnego.
Rozważmy kwadrat o boku \(2r\), w który wpisane jest koło o promieniu \(r\). Pole kwadratu wynosi \(P_{kwadratu} = (2r)^2 = 4r^2\), natomiast pole koła to \(P_{kola} = \pi r^2\). Stosunek pola koła do pola kwadratu wyraża się wzorem:
Jeżeli wygenerujemy \(N\) niezależnych punktów o współrzędnych \((x, y)\) z rozkładu jednostajnego \(U(-r, r)\), to prawdopodobieństwo, że punkt znajdzie się wewnątrz koła (czyli spełni warunek \(x^2 + y^2 \le r^2\)), jest równe stosunkowi tych pól. Oznaczając liczbę punktów, które trafiły do wnętrza koła jako \(M\), otrzymujemy przybliżenie:
\[\pi \approx 4 \frac{M}{N}\]
Poniższy kod w języku R przeprowadza taką symulację dla \(r=1\) i \(N=10000\) iteracji, a następnie wizualizuje wynik:
Code
N <-10000x <-runif(N, min =-1, max =1)y <-runif(N, min =-1, max =1)w_kole <- (x^2+ y^2) <=1M <-sum(w_kole)pi_est <-4* M / Ncat("Oszacowana wartość pi dla N =", N, "wynosi:", pi_est, "\n")
Oszacowana wartość pi dla N = 10000 wynosi: 3.166
Code
cat("Błąd bezwzględny:", abs(pi - pi_est), "\n")
Błąd bezwzględny: 0.02440735
Code
plot(x, y, col =ifelse(w_kole, "steelblue", "red"), pch =20, cex =0.5,main =paste("Szacowanie liczby pi (Wynik:", pi_est, ")"),xlab ="x", ylab ="y", asp =1)theta <-seq(0, 2*pi, length.out =100)lines(cos(theta), sin(theta), col ="black", lwd =2)
Wizualizacja szacowania liczby pi metodą Monte Carlo
Eksperyment Igły Buffona
Innym, klasycznym przykładem estymacji liczby \(\pi\) z wykorzystaniem symulacji jest tzw. problem igły Buffona. Zakłada on rzucanie igły o długości \(l\) na płaszczyznę pokrytą równoległymi liniami oddalonymi od siebie o odległość \(d\) (przy czym \(l \le d\)).
Zgodnie z klasycznymi dowodami, prawdopodobieństwo tego, że igła przetnie którąkolwiek z linii, wyraża się wzorem: \[P = \frac{2l}{\pi d}\] Przekształcając ten wzór i estymując prawdopodobieństwo na podstawie wyników symulacji komputerowej (stosunek rzutów przecinających linie do wszystkich rzutów), możemy oszacować wartość \(\pi\). Poniżej znajduje się implementacja tego eksperymentu:
Code
set.seed(123)N <-100000l <-1d <-2alfa <-runif(N) * pix <-runif(N) * dprzeciecia <-sum((x +0.5* l *sin(alfa) > d) | (x -0.5* l *sin(alfa) <0))pi_buffon <-2* l * N / (d * przeciecia)cat("Oszacowana wartość pi (Igła Buffona) dla N =", N, "wynosi:", pi_buffon, "\n")
Oszacowana wartość pi (Igła Buffona) dla N = 1e+05 wynosi: 3.132538
Generatory wartości z rozkładu U[0, 1]
Współczesne symulacje numeryczne opierają się na programowych generatorach liczb pseudolosowych. Zgodnie z literaturą przedmiotu (T. Chwiej, Generatory liczb pseudolosowych, AGH), w przeciwieństwie do generatorów fizycznych (wykorzystujących np. zjawisko promieniotwórczości czy szumy elektroniczne), algorytmy komputerowe charakteryzują się powtarzalnością generowanych ciągów oraz dużą łatwością obsługi.
Jak w praktyce pracują takie algorytmy? W pierwszym etapie tworzony jest deterministyczny ciąg liczb naturalnych \(X_0, X_1, \dots, X_n\), które są ograniczone od góry przez określoną reprezentację maszynową \(m\) (np. \(m = 2^{32}\) dla 32-bitowej liczby całkowitej). Następnie, aby uzyskać pożądane wartości rzeczywiste mieszczące się w przedziale \((0, 1]\), stosuje się prostą transformację:
\[U = \frac{X}{m} \implies U_i \in (0, 1]\]
Warto w tym miejscu podkreślić fundamentalną różnicę między prawdziwą losowością a tą generowaną przez maszyny. Jak zauważają R. Wieczorkowski i R. Zieliński w swojej klasycznej monografii, ciągiem ściśle losowym jest taki ciąg, którego nie da się zapisać za pomocą algorytmu krótszego niż on sam. Komputery są urządzeniami deterministycznymi, dlatego generują tzw. liczby pseudolosowe.
Konsekwencją tego jest fakt, że źle zaprojektowany generator (np. o nieodpowiednio dobranych parametrach mnożnika) wyprodukuje ciąg silnie schematyczny. Tego typu “niepoprawne” generatory charakteryzują się bardzo krótkimi cyklami lub ukrytymi regularnościami – wygenerowane z nich liczby, po naniesieniu na wykres wielowymiarowy, zaczynają układać się w przewidywalne równoległe proste lub hiperpłaszczyzny, co całkowicie dyskwalifikuje je z poważnych symulacji Monte Carlo.
Metoda środków kwadratów
Dobrym przykładem obrazującym samą ideę generowania takich ciągów z pewnej wartości początkowej (ziarna) jest klasyczna metoda środków kwadratów. Polega ona na podnoszeniu liczby do potęgi drugiej, wyodrębnianiu jej środkowych cyfr, a następnie dzieleniu przez odpowiednią potęgę dziesiątki (pełniącą tu rolę naszego \(m\)), co sprowadza wynik do przedziału \((0, 1)\).
Code
x <-8234372336y <-c()for (i in1:10) { x <- x^2# Zabezpieczenie przed notacją naukową w R x_str <-format(x, scientific =FALSE) x_str <-substr(x_str, 6, 15) x <-as.numeric(x_str) y <-c(y, x /10^10) }print(y)
Znacznie bardziej sformalizowaną i szeroko opisaną klasą algorytmów są generatory liniowe. Opierają się one na operacji dzielenia modulo (wyznaczania reszty z dzielenia), tworząc ciąg według rekurencyjnego schematu:
Gdzie parametry generatora (\(a_i, c, m\)) są z góry ustalonymi liczbami całkowitymi. Najprostszą i niezwykle popularną odmianą jest tzw. generator multiplikatywny, w którym stała \(c = 0\), a wzór redukuje się do postaci:
\[X_{i+1} = a X_{i-1} \pmod m\]
W tego typu algorytmach kluczowy jest dobór odpowiednich parametrów, ponieważ to od nich zależy tzw. okres generatora, czyli moment, w którym sekwencja liczb zacznie się powtarzać. Maksymalny możliwy okres dla generatora multiplikatywnego wynosi \(m-1\).
Poniżej przedstawiono przykład wygenerowania dużej próby takich liczb w środowisku R (przy użyciu domyślnych funkcji optymalizowanych):
Warto również wspomnieć, że suma niezależnych zmiennych losowych o rozkładzie jednostajnym \(U(0, 1)\) tworzy rozkład, który zgodnie z Centralnym Twierdzeniem Granicznym (CTG) dąży do rozkładu normalnego. Należy przy tym pamiętać, że zbieżność ta następuje wraz ze wzrostem liczby sumowanych zmiennych losowych. W szczególnym przypadku, gdy sumujemy dokładnie dwie niezależne zmienne o rozkładzie \(U(0, 1)\), otrzymujemy precyzyjny rozkład trójkątny określony na przedziale \((0, 2)\):
Code
set.seed(42)hist(runif(100000) +runif(100000), main ="Histogram sumy dwóch rozkładów jednostajnych",xlab ="Wartość z przedziału (0, 2)", ylab ="Częstość", col ="darkgreen")
Generatory wartości z zadanego rozkładu
Dysponując wartościami losowymi z rozkładu jednostajnego \(U(0,1)\), możemy wykorzystać odpowiednie metody transformacji, aby otrzymać wartości losowe z dowolnych innych rozkładów. Można tego dokonać na 4 różne metody:
Metoda odwracania dystrybuanty
Metoda ta opiera się na własnościach dystrybuanty \(F(x)\), która zawsze przyjmuje wartości z przedziału \([0, 1]\). Algorytm jest prosty: losujemy wartość \(U\) z rozkładu jednostajnego \(U(0, 1)\), a następnie przyrównujemy ją do dystrybuanty docelowego rozkładu i wyliczamy \(x\). Matematycznie sprowadza się to do użycia funkcji odwrotnej (tzw. funkcji kwantylowej): \[x = F^{-1}(U)\]
W środowisku R funkcja odwrotna do dystrybuanty jest często wbudowana jako np. qnorm. Poniżej przykład ręcznego odwrócenia dystrybuanty dla rozkładu wykładniczego, gdzie \(F(x) = 1 - e^{-x}\), co po przekształceniu daje wzór \(x = -\ln(1-U)\).
Code
set.seed(123)n <-10000U <-runif(n)X <--log(1- U) hist(X, prob =TRUE, breaks =40, col ="lightyellow", main ="Rozkład wykładniczy (Odwracanie dystrybuanty)", xlab="x")
Metoda eliminacji (akceptacji-odrzucenia)
W momencie gdy znalezienie funkcji odwrotnej do dystrybuanty jest trudne lub niemożliwe, można skorzystać z metody eliminacji. Wymaga ona znajomości funkcji gęstości \(f(x)\) oraz jej maksimum na rozpatrywanym przedziale.
Doskonałym przykładem zastosowania tej metody jest generowanie wartości z rozkładu Beta o parametrach (2,2). Funkcja gęstości tego rozkładu na przedziale \([0, 1]\) ma postać \(f(x) = 6x(1-x)\), a jej maksymalna wartość (wierzchołek paraboli) wynosi \(1.5\) (czyli \(3/2\)).
Algorytm losuje punkt \((x, y)\) w prostokącie ograniczającym funkcję i akceptuje go tylko wtedy, gdy punkt “trafi” pod wykres gęstości. W przeciwnym razie losowanie powtarzane jest aż do skutku:
Code
set.seed(123)n <-1000wygenerowane_beta <-numeric(n)licznik <-1while (licznik <= n) { x <-runif(1, 0, 1) y <-runif(1, 0, 3/2) fx <-6* x * (1- x) if (y < fx) { wygenerowane_beta[licznik] <- x licznik <- licznik +1 }}hist(wygenerowane_beta, prob=TRUE, col="lightblue", main="Metoda eliminacji (Rozkład Beta)", xlab="x", ylab="Gęstość")curve(6*x*(1-x), add=TRUE, col="red", lwd=2)
Metoda odrzucania dla rozkładów warunkowych
Metoda ta jest specyficzną modyfikacją klasycznej eliminacji. Znajduje zastosowanie w sytuacji, gdy celem jest wygenerowanie zmiennej z tzw. rozkładu uciętego. Proces polega na losowaniu wartości z pełnego rozkładu bazowego, a następnie filtrowaniu wyników – odrzucane są wszystkie te, które nie spełniają zadanego logicznego warunku brzegowego.
Metoda superpozycji jest stosowana, gdy docelowa gęstość prawdopodobieństwa stanowi kombinację liniową dwóch lub więcej innych gęstości. Sam proces symulacji jest dwuetapowy: najpierw z zadanym prawdopodobieństwem (wagą) losowany jest rozkład składowy, z którego w danej iteracji będzie pochodzić wynik, a w drugim etapie następuje właściwe wygenerowanie wartości z wybranego rozkładu.
Przykład praktyczny symulacji: Rozpatrzmy symulację czasu przybycia klientów do restauracji w ciągu dnia. Z empirycznych obserwacji wynika, że rozkład ten jest bimodalny i skupia się wokół dwóch szczytów natężenia ruchu: 1. Porannego (kawa wokół godziny 9:00, co stanowi 30% całkowitego ruchu). 2. Popołudniowego (obiad wokół godziny 14:00, co stanowi 70% całkowitego ruchu).
Code
set.seed(123)n_klientow <-10000czasy_przybycia <-numeric(n_klientow)for(i in1:n_klientow) {# Etap 1: Wybór rozkładu składowego (za pomocą zmiennej U(0,1))if(runif(1) <0.30) {# 30% szans: Generowanie czasu dla szczytu porannego czasy_przybycia[i] <-rnorm(1, mean =9, sd =1.2) } else {# 70% szans: Generowanie czasu dla szczytu popołudniowego czasy_przybycia[i] <-rnorm(1, mean =14, sd =2.0) }}hist(czasy_przybycia, prob =TRUE, breaks =40, col ="purple3",main ="Bimodalny czas przybycia klientów (Superpozycja)",xlab ="Godzina w ciągu dnia", ylab ="Częstość")lines(density(czasy_przybycia), col ="darkred", lwd =2)
Testy normalności
Zarówno przy badaniu rzeczywistych danych, jak i przy weryfikacji poprawności działania naszych generatorów, kluczowe jest sprawdzenie, czy otrzymana próba faktycznie pochodzi z rozkładu normalnego.
Do klasycznych metod wnioskowania statystycznego w tym zakresie zaliczamy testy normalności. Przyjmują one jako hipotezę zerową (\(H_0\)) założenie, że dane pochodzą z populacji o rozkładzie normalnym. Poniżej przedstawiono weryfikację rozkładu na przykładzie realnego zbioru danych mtcars, zmienna mpgjako spalanie w milach na galon z wykorzystaniem popularnych testów: Kołmogorowa-Smirnowa oraz Shapiro-Wilka.
Code
dane_mpg <- mtcars$mpghist(dane_mpg, prob=TRUE, breaks=10, col="darkred", main="Histogram dla zmiennej mpg ze zbioru mtcars", xlab="MPG")lines(density(dane_mpg), col="darkgreen", lwd=2)
Shapiro-Wilk normality test
data: dane_mpg
W = 0.94756, p-value = 0.1229
Interpretacja: W obu przeprowadzonych testach wartość p-value jest większa od standardowego poziomu istotności \(\alpha = 0.05\) (dla testu Shapiro-Wilka wynosi 0.1229). Oznacza to, że nie mamy podstaw do odrzucenia hipotezy zerowej – możemy zatem przyjąć, że zmienna mpg w zbiorze mtcars ma rozkład zbliżony do normalnego.
Bibliografia
Chwiej, T. (2022/2023). Generatory liczb pseudolosowych. Materiały do wykładu z Metod Numerycznych, Akademia Górniczo-Hutnicza w Krakowie (AGH). Dostępne online: https://galaxy.agh.edu.pl/~chwiej/mn/wyk/generatory_22_23.pdf
Wieczorkowski, R., Zieliński, R. (1997). Komputerowe generatory liczb losowych. Warszawa: Wydawnictwa Naukowo-Techniczne (WNT), strony: 15-38.
Bobrowski, D. (2004). Modele i metody matematyczne w symulacji komputerowej. Poznań: Wydawnictwo Naukowe UAM, strony: 55-82.
Source Code
---title: "Zadanie 1 NMAD"author: "Wojciech Potrawa"format: html: theme: minty toc: true toc-title: "Spis treści" code-fold: true code-tools: trueeditor: source---## Szacowanie liczby $\pi$###Strzały do tarczy Szacowanie wartości liczby $\pi$ metodą Monte Carlo opiera się na geometrycznym prawdopodobieństwie trafienia losowego punktu w określony obszar. Proces ten wykorzystuje generowanie punktów z rozkładu jednostajnego.Rozważmy kwadrat o boku $2r$, w który wpisane jest koło o promieniu $r$. Pole kwadratu wynosi $P_{kwadratu} = (2r)^2 = 4r^2$, natomiast pole koła to $P_{kola} = \pi r^2$. Stosunek pola koła do pola kwadratu wyraża się wzorem:$$\frac{P_{kola}}{P_{kwadratu}} = \frac{\pi r^2}{4r^2} = \frac{\pi}{4}$$Jeżeli wygenerujemy $N$ niezależnych punktów o współrzędnych $(x, y)$ z rozkładu jednostajnego $U(-r, r)$, to prawdopodobieństwo, że punkt znajdzie się wewnątrz koła (czyli spełni warunek $x^2 + y^2 \le r^2$), jest równe stosunkowi tych pól. Oznaczając liczbę punktów, które trafiły do wnętrza koła jako $M$, otrzymujemy przybliżenie:$$\pi \approx 4 \frac{M}{N}$$Poniższy kod w języku R przeprowadza taką symulację dla $r=1$ i $N=10000$ iteracji, a następnie wizualizuje wynik:```{r}#| label: pi-estimation#| fig-cap: "Wizualizacja szacowania liczby pi metodą Monte Carlo"#| warning: falseN <-10000x <-runif(N, min =-1, max =1)y <-runif(N, min =-1, max =1)w_kole <- (x^2+ y^2) <=1M <-sum(w_kole)pi_est <-4* M / Ncat("Oszacowana wartość pi dla N =", N, "wynosi:", pi_est, "\n")cat("Błąd bezwzględny:", abs(pi - pi_est), "\n")plot(x, y, col =ifelse(w_kole, "steelblue", "red"), pch =20, cex =0.5,main =paste("Szacowanie liczby pi (Wynik:", pi_est, ")"),xlab ="x", ylab ="y", asp =1)theta <-seq(0, 2*pi, length.out =100)lines(cos(theta), sin(theta), col ="black", lwd =2)```### Eksperyment Igły BuffonaInnym, klasycznym przykładem estymacji liczby $\pi$ z wykorzystaniem symulacji jest tzw. problem igły Buffona. Zakłada on rzucanie igły o długości $l$ na płaszczyznę pokrytą równoległymi liniami oddalonymi od siebie o odległość $d$ (przy czym $l \le d$). Zgodnie z klasycznymi dowodami, prawdopodobieństwo tego, że igła przetnie którąkolwiek z linii, wyraża się wzorem:$$P = \frac{2l}{\pi d}$$Przekształcając ten wzór i estymując prawdopodobieństwo na podstawie wyników symulacji komputerowej (stosunek rzutów przecinających linie do wszystkich rzutów), możemy oszacować wartość $\pi$. Poniżej znajduje się implementacja tego eksperymentu:```{r}#| label: buffon-needleset.seed(123)N <-100000l <-1d <-2alfa <-runif(N) * pix <-runif(N) * dprzeciecia <-sum((x +0.5* l *sin(alfa) > d) | (x -0.5* l *sin(alfa) <0))pi_buffon <-2* l * N / (d * przeciecia)cat("Oszacowana wartość pi (Igła Buffona) dla N =", N, "wynosi:", pi_buffon, "\n")```## Generatory wartości z rozkładu U\[0, 1\]Współczesne symulacje numeryczne opierają się na programowych generatorach liczb pseudolosowych. Zgodnie z literaturą przedmiotu (T. Chwiej, [Generatory liczb pseudolosowych](https://galaxy.agh.edu.pl/~chwiej/mn/wyk/generatory_22_23.pdf), AGH), w przeciwieństwie do generatorów fizycznych (wykorzystujących np. zjawisko promieniotwórczości czy szumy elektroniczne), algorytmy komputerowe charakteryzują się powtarzalnością generowanych ciągów oraz dużą łatwością obsługi.Jak w praktyce pracują takie algorytmy? W pierwszym etapie tworzony jest deterministyczny ciąg liczb naturalnych $X_0, X_1, \dots, X_n$, które są ograniczone od góry przez określoną reprezentację maszynową $m$ (np. $m = 2^{32}$ dla 32-bitowej liczby całkowitej). Następnie, aby uzyskać pożądane wartości rzeczywiste mieszczące się w przedziale $(0, 1]$, stosuje się prostą transformację:$$U = \frac{X}{m} \implies U_i \in (0, 1]$$Warto w tym miejscu podkreślić fundamentalną różnicę między prawdziwą losowością a tą generowaną przez maszyny. Jak zauważają R. Wieczorkowski i R. Zieliński w swojej klasycznej monografii, ciągiem ściśle losowym jest taki ciąg, którego nie da się zapisać za pomocą algorytmu krótszego niż on sam. Komputery są urządzeniami deterministycznymi, dlatego generują tzw. liczby **pseudolosowe**. Konsekwencją tego jest fakt, że źle zaprojektowany generator (np. o nieodpowiednio dobranych parametrach mnożnika) wyprodukuje ciąg silnie schematyczny. Tego typu "niepoprawne" generatory charakteryzują się bardzo krótkimi cyklami lub ukrytymi regularnościami – wygenerowane z nich liczby, po naniesieniu na wykres wielowymiarowy, zaczynają układać się w przewidywalne równoległe proste lub hiperpłaszczyzny, co całkowicie dyskwalifikuje je z poważnych symulacji Monte Carlo.### Metoda środków kwadratówDobrym przykładem obrazującym samą ideę generowania takich ciągów z pewnej wartości początkowej (ziarna) jest klasyczna metoda środków kwadratów. Polega ona na podnoszeniu liczby do potęgi drugiej, wyodrębnianiu jej środkowych cyfr, a następnie dzieleniu przez odpowiednią potęgę dziesiątki (pełniącą tu rolę naszego $m$), co sprowadza wynik do przedziału $(0, 1)$.```{r}x <-8234372336y <-c()for (i in1:10) { x <- x^2# Zabezpieczenie przed notacją naukową w R x_str <-format(x, scientific =FALSE) x_str <-substr(x_str, 6, 15) x <-as.numeric(x_str) y <-c(y, x /10^10) }print(y)```### Liniowe generatoryZnacznie bardziej sformalizowaną i szeroko opisaną klasą algorytmów są generatory liniowe. Opierają się one na operacji dzielenia modulo (wyznaczania reszty z dzielenia), tworząc ciąg według rekurencyjnego schematu:$$X_{n+1} = (a_1 X_n + a_2 X_{n-1} + \dots + a_k X_{n-k+1} + c) \pmod m$$Gdzie parametry generatora ($a_i, c, m$) są z góry ustalonymi liczbami całkowitymi. Najprostszą i niezwykle popularną odmianą jest tzw. generator multiplikatywny, w którym stała $c = 0$, a wzór redukuje się do postaci:$$X_{i+1} = a X_{i-1} \pmod m$$W tego typu algorytmach kluczowy jest dobór odpowiednich parametrów, ponieważ to od nich zależy tzw. okres generatora, czyli moment, w którym sekwencja liczb zacznie się powtarzać. Maksymalny możliwy okres dla generatora multiplikatywnego wynosi $m-1$.Poniżej przedstawiono przykład wygenerowania dużej próby takich liczb w środowisku R (przy użyciu domyślnych funkcji optymalizowanych):```{r}set.seed(123)u_vals <-runif(100000)hist(u_vals, main="Histogram rozkładu jednostajnego", xlab="Wartość z przedziału (0, 1)", ylab="Częstość", col="steelblue")```Warto również wspomnieć, że suma niezależnych zmiennych losowych o rozkładzie jednostajnym $U(0, 1)$ tworzy rozkład, który zgodnie z Centralnym Twierdzeniem Granicznym (CTG) dąży do rozkładu normalnego. Należy przy tym pamiętać, że zbieżność ta następuje wraz ze wzrostem liczby sumowanych zmiennych losowych. W szczególnym przypadku, gdy sumujemy dokładnie dwie niezależne zmienne o rozkładzie $U(0, 1)$, otrzymujemy precyzyjny rozkład trójkątny określony na przedziale $(0, 2)$:```{r}set.seed(42)hist(runif(100000) +runif(100000), main ="Histogram sumy dwóch rozkładów jednostajnych",xlab ="Wartość z przedziału (0, 2)", ylab ="Częstość", col ="darkgreen")```## Generatory wartości z zadanego rozkładuDysponując wartościami losowymi z rozkładu jednostajnego $U(0,1)$, możemy wykorzystać odpowiednie metody transformacji, aby otrzymać wartości losowe z dowolnych innych rozkładów. Można tego dokonać na 4 różne metody:### Metoda odwracania dystrybuantyMetoda ta opiera się na własnościach dystrybuanty $F(x)$, która zawsze przyjmuje wartości z przedziału $[0, 1]$. Algorytm jest prosty: losujemy wartość $U$ z rozkładu jednostajnego $U(0, 1)$, a następnie przyrównujemy ją do dystrybuanty docelowego rozkładu i wyliczamy $x$.Matematycznie sprowadza się to do użycia funkcji odwrotnej (tzw. funkcji kwantylowej):$$x = F^{-1}(U)$$W środowisku R funkcja odwrotna do dystrybuanty jest często wbudowana jako np. qnorm. Poniżej przykład ręcznego odwrócenia dystrybuanty dla rozkładu wykładniczego, gdzie $F(x) = 1 - e^{-x}$, co po przekształceniu daje wzór $x = -\ln(1-U)$.```{r}set.seed(123)n <-10000U <-runif(n)X <--log(1- U) hist(X, prob =TRUE, breaks =40, col ="lightyellow", main ="Rozkład wykładniczy (Odwracanie dystrybuanty)", xlab="x")```### Metoda eliminacji (akceptacji-odrzucenia)W momencie gdy znalezienie funkcji odwrotnej do dystrybuanty jest trudne lub niemożliwe, można skorzystać z metody eliminacji. Wymaga ona znajomości funkcji gęstości $f(x)$ oraz jej maksimum na rozpatrywanym przedziale. Doskonałym przykładem zastosowania tej metody jest generowanie wartości z rozkładu Beta o parametrach (2,2). Funkcja gęstości tego rozkładu na przedziale $[0, 1]$ ma postać $f(x) = 6x(1-x)$, a jej maksymalna wartość (wierzchołek paraboli) wynosi $1.5$ (czyli $3/2$). Algorytm losuje punkt $(x, y)$ w prostokącie ograniczającym funkcję i akceptuje go tylko wtedy, gdy punkt "trafi" pod wykres gęstości. W przeciwnym razie losowanie powtarzane jest aż do skutku:```{r}set.seed(123)n <-1000wygenerowane_beta <-numeric(n)licznik <-1while (licznik <= n) { x <-runif(1, 0, 1) y <-runif(1, 0, 3/2) fx <-6* x * (1- x) if (y < fx) { wygenerowane_beta[licznik] <- x licznik <- licznik +1 }}hist(wygenerowane_beta, prob=TRUE, col="lightblue", main="Metoda eliminacji (Rozkład Beta)", xlab="x", ylab="Gęstość")curve(6*x*(1-x), add=TRUE, col="red", lwd=2)```### Metoda odrzucania dla rozkładów warunkowychMetoda ta jest specyficzną modyfikacją klasycznej eliminacji. Znajduje zastosowanie w sytuacji, gdy celem jest wygenerowanie zmiennej z tzw. rozkładu uciętego. Proces polega na losowaniu wartości z pełnego rozkładu bazowego, a następnie filtrowaniu wyników – odrzucane są wszystkie te, które nie spełniają zadanego logicznego warunku brzegowego.```{r}set.seed(123)warunkowe_x <-numeric(0)while(length(warunkowe_x) <2000) { kandydaci <-rnorm(500) zaakceptowani <- kandydaci[kandydaci >0] warunkowe_x <-c(warunkowe_x, zaakceptowani)}warunkowe_x <- warunkowe_x[1:2000] hist(warunkowe_x, prob=TRUE, breaks=30, col="lavender",main="Warunkowy rozkład normalny (ucinka x > 0)", xlab="x", ylab="Gęstość")```### Metoda superpozycji rozkładówMetoda superpozycji jest stosowana, gdy docelowa gęstość prawdopodobieństwa stanowi kombinację liniową dwóch lub więcej innych gęstości. Sam proces symulacji jest dwuetapowy: najpierw z zadanym prawdopodobieństwem (wagą) losowany jest rozkład składowy, z którego w danej iteracji będzie pochodzić wynik, a w drugim etapie następuje właściwe wygenerowanie wartości z wybranego rozkładu.**Przykład praktyczny symulacji:** Rozpatrzmy symulację czasu przybycia klientów do restauracji w ciągu dnia. Z empirycznych obserwacji wynika, że rozkład ten jest bimodalny i skupia się wokół dwóch szczytów natężenia ruchu: 1. Porannego (kawa wokół godziny 9:00, co stanowi 30% całkowitego ruchu).2. Popołudniowego (obiad wokół godziny 14:00, co stanowi 70% całkowitego ruchu).```{r}#| label: superposition-custom-exampleset.seed(123)n_klientow <-10000czasy_przybycia <-numeric(n_klientow)for(i in1:n_klientow) {# Etap 1: Wybór rozkładu składowego (za pomocą zmiennej U(0,1))if(runif(1) <0.30) {# 30% szans: Generowanie czasu dla szczytu porannego czasy_przybycia[i] <-rnorm(1, mean =9, sd =1.2) } else {# 70% szans: Generowanie czasu dla szczytu popołudniowego czasy_przybycia[i] <-rnorm(1, mean =14, sd =2.0) }}hist(czasy_przybycia, prob =TRUE, breaks =40, col ="purple3",main ="Bimodalny czas przybycia klientów (Superpozycja)",xlab ="Godzina w ciągu dnia", ylab ="Częstość")lines(density(czasy_przybycia), col ="darkred", lwd =2)```## Testy normalności Zarówno przy badaniu rzeczywistych danych, jak i przy weryfikacji poprawności działania naszych generatorów, kluczowe jest sprawdzenie, czy otrzymana próba faktycznie pochodzi z rozkładu normalnego. Do klasycznych metod wnioskowania statystycznego w tym zakresie zaliczamy testy normalności. Przyjmują one jako hipotezę zerową ($H_0$) założenie, że dane pochodzą z populacji o rozkładzie normalnym. Poniżej przedstawiono weryfikację rozkładu na przykładzie realnego zbioru danych `mtcars`, zmienna `mpg`jako spalanie w milach na galon z wykorzystaniem popularnych testów: Kołmogorowa-Smirnowa oraz Shapiro-Wilka.```{r}#| warning: falsedane_mpg <- mtcars$mpghist(dane_mpg, prob=TRUE, breaks=10, col="darkred", main="Histogram dla zmiennej mpg ze zbioru mtcars", xlab="MPG")lines(density(dane_mpg), col="darkgreen", lwd=2)ks_wynik <-ks.test(dane_mpg, "pnorm", mean(dane_mpg), sd(dane_mpg))print(ks_wynik)shapiro_wynik <-shapiro.test(dane_mpg)print(shapiro_wynik)```**Interpretacja:**W obu przeprowadzonych testach wartość p-value jest większa od standardowego poziomu istotności $\alpha = 0.05$ (dla testu Shapiro-Wilka wynosi `0.1229`). Oznacza to, że nie mamy podstaw do odrzucenia hipotezy zerowej – możemy zatem przyjąć, że zmienna `mpg` w zbiorze `mtcars` ma rozkład zbliżony do normalnego.## Bibliografia1. Chwiej, T. (2022/2023). *Generatory liczb pseudolosowych*. Materiały do wykładu z Metod Numerycznych, Akademia Górniczo-Hutnicza w Krakowie (AGH). Dostępne online: https://galaxy.agh.edu.pl/~chwiej/mn/wyk/generatory_22_23.pdf2. Wieczorkowski, R., Zieliński, R. (1997). *Komputerowe generatory liczb losowych*. Warszawa: Wydawnictwa Naukowo-Techniczne (WNT), strony: 15-38. 3. Bobrowski, D. (2004). *Modele i metody matematyczne w symulacji komputerowej*. Poznań: Wydawnictwo Naukowe UAM, strony: 55-82.