MATLAB Tutorial #8

Programowanie w MATLABie

W poprzedniej części poradnika pokazałem, jak w MATLABie można ułatwić sobie prace dzięki tworzeniu skryptów. Skrypty umożliwiają zebranie szeregu instrukcji w ramach jednego pliku tekstowego, ich opisanie, wywołanie i w końcu, w razie potrzeby, łatwą edycję. Ale to nie wszystko. Skryptu umożliwiają również wprowadzenie do tworzonych procedur różnych instrukcji sterujących, dzięki czemu MATLAB może być traktowany jako język programowania.

Paradoks Monty'ego Halla

Pretekstem do wprowadzenie do pracy z instrukcjami sterującymi bedzie próba rozgryzienia ciekawego paradoksu teleturniejowo – statystycznego. Paradoks Monty'ego Halla ujawnił się dzięki telewizyjnemu show „Let's make a deal”, którego mutacja trafiła pod koniec lat 90 na ekrany polskich telewizorów pod nazwą „Idź na całość”. Momentem kulminacyjnym tego jakże barwnego teleturnieju (polecam przegląd archiwów na youtube) była „walka” o samochód. Upraszczając, wyglądało to tak: prowadzący program prosił uczestnika o wskazanie jednej z trzech bramek informując, że za jedną z tych bramek umieszczony został samochód – nagroda główna. Uczestnik wskazywał wybraną przez siebie bramkę (wybór był losowy). Prowadzący, w celu pogrzania atmosfery często odsłaniał jedną z bramek nie wybranych przez uczestnika, bramkę, za którą nie znajdowała się nagroda główna. Odsłonięta bramka mogła być pusta lub mogła zawierać pomniejszą nagrodę typu mikrofalówka, lodówka czy inny odkurzacz. W tym momencie ujawniała się cała dramaturgia przedstawienia: prowadzący (w wersji polskiej Zygmunt Chajzer) zwykł mówić uczestnikowi, że może zmienić swój pierwotny wybór na pozostałą nieodsłoniętą bramkę. Pytanie: czy uczestnikowi opłaca się dokonać zmiany? intuicyjne, wydaje nam się, że nie ma to znaczenia. W rzeczywistości… okazuje się, że postawieni przed takim wyborem powinniśmy skorzystać z możliwości zmiany, bo to zwiększa nasze szanse na wygranie samochodu!. O paradoksie Monty’ego Halla można przeczytać w Wikipedii ale zrozumienie problemu ułatwi też symulacja, którą zaprojektujemy sobie teraz w MATLAB’ie.

Od linii poleceń do skryptu.

Pracę nad problemem możemy zacząć w oknie Command Window, co ułatwi nam prototypowanie właściwego rozwiązania. Rozpoczniemy od prostszego przypadku, zakładamy że uczestnik teleturnieju jest uparciuchem i za żadne skarby nie będzie chciał zmienić swojego pierwotnego wyboru. Zaczynamy.
Pierwszą rzeczą, którą musimy zrobić, to przygotować bramki. Będą one reprezentowane przez 3 elementowy wektor. Następnie losowo wybieramy bramkę, do której trafia samochód, po czym pojazd podjeżdża w odpowiednie miejsce.

  • Wprowadź w linii poleceń MATLAB’a:
bramki = [false false false];
bramka_z_nagroda = randi(3); % polecenie losuję liczbę naturalną w zakresie 1 - 3 
bramki(bramka_z_nagroda) = true

bramki =

0 0 1

Scena została przygotowana, teraz wchodzi na nią gracz. Jego zadanie, to wskazanie bramki, z nagrodą. Oczywiście jego wskazanie jest czysto losowe.

  • Zasymuluj wybór gracza.
pierwszy_wybor = randi(3)

pierwszy_wybor =

2

Prowadzący zaczyn odgrywać przedstawienie, pokazuje pustą bramkę, proponuje zmianę, Ty jednak nie musisz tego obecnie symulować, bo to nic nie zmienia. Gracz pozostaje wierny swojemu pierwotnemu wyborowi. Czy wygrał? Możesz to sprawdzić wpisując w linii poleceń:

wygrana = bramki(pierwszy_wybor)

wygrana =

0

Wywołanie powyższej instrukcji odpowie nam na pytanie: Czy w bramce będącej pierwszym wyborem gracza znajduje się samochód. Jeżeli w tej rozgrywce gracz wygrał, zmienna wygrana przyjmie wartość 1, w przeciwnym wypadku 0.
Tym razem się nie udało... W każdym razie w mojej symulacji. Ponieważ w grę wchodzi czynnik losowy, wykonując powyższy kod na swoim komputerze możesz osiągnąć inne rezultaty!

Teraz, żeby oszacować skuteczność strategii „pierwszy wybór” należy powtórzyć powyższą symulację N razy i sprawdzić jak często na końcu zmienna wygrana przyjmuje wartość 1. W tym momencie korzystne będzie stworzenie skryptu, który zautomatyzuje nam proces.

  • Utwórz krypt monty_uparciuch.m i umieść w nim cały dotychczasowo przedstawiony kod.

Każde wciśnięcie przycisku „Run” powoduje sekwencyjne wykonanie wszystkich poleceń zawartych w skrypcie. Ułatwia nam to znacznie przeprowadzenie pojedynczej symulacji, jednak to, co chcemy osiągnąć, to przeprowadzenie kilkudziesięciu, a może nawet kilku tysięcy symulacji. W tym celu posłużymy się konstrukcją zwaną pętlą.

Pętla FOR

Pętla for przydaje się, gdy chcemy wykonać określoną grupę instrukcji n- razy. Posłużymy się teraz pętlą w naszym skrypcie. Zmodyfikuj skrypt tak, żeby wyglądał jak poniżej:

liczba_gier = 10000;
for i = 1:liczba_gier;
bramki = [false false false];
bramka_z_nagroda = randi(3); % polecenie losuje liczbę naturalną w zakresie 1 - 3 
bramki(bramka_z_nagroda) = true;
pierwszy_wybor = randi(3);
wygrana = bramki(pierwszy_wybor);
end

Po wciśnięciu przycisku Run kod pomiędzy linią for a end zostanie wykonany 10000 razy (Tyle wynosi wartość zmiennej liczba_gier). Niestety, nie będziemy mieli z tego większego pożytku, gdyż wyniki poszczególnych symulacji nie są nigdzie zapisywane. Potrzebne będą pewne drobne modyfikacje skryptu. Przed pętlą for zainicjuj zmienną liczba_wygranych przypisując jej wartość 0, natomiast tuż przed słowem end, zamykającym ciało pętli, wprowadź liczba_wygranych = liczba_wygranych + wygrana;
W ten sposób, przy każdym przebiegu pętli for zmienna liczba_wygranych będzie aktualizowana zgodnie z wynikiem symulacji. Na koniec jeszcze jedna zmiana estetyczna. Zaznacz wszystkie elementy skryptu wciskając skrót klawiszowy ctrl + A, po czym w części EIDT zakładki EDITOR kliknij na przycisk Apply smart indent (możesz też posłużyć się kolejnym skrótem klawiszowym ctrl + I). Skrypt dzięki temu robi się czytelniejszy.

liczba_gier = 10000;
liczba_wygranych = 0;
for i = 1:ilosc_gier
    bramki = [false false false];
    bramka_z_nagroda = randi(3); % polecenie losuję liczbę naturalną w zakresie 1 - 3
    bramki(bramka_z_nagroda) = true;
    pierwszy_wybor = randi(3);
    wygrana = bramki(pierwszy_wybor);
    liczba_wygranych = liczba_wygranych + wygrana;
end

Możesz teraz wcisnąć przycisk Run, obliczenia powinny wykonać się błyskawicznie. W przestrzeni roboczej masz teraz dwie ciekawe zmienne: liczba_gier oraz liczba_wygranych. Jeśli podzielisz jedną przez drugą będziesz znał przybliżone prawdopodobieństwo wygrania samochodu przy zachowaniu strategii uparciucha.

  • Korzystając z wyników symulacji wyznacz prawdopodobieństwo wygranej
liczba_wygranych / liczba_gier

Wynik powinien wynosić w przybliżeniu 1/3. Tego się zresztą spodziewaliśmy, o wiele ciekawsze będzie empiryczne wyznaczenie prawdopodobieństwa wygranej dla strategii „zmień jeśli możesz”. O tym w następnej części poradnika. Teraz na zakończenie jedna uwaga (wcale nie mała). Powyższą symulacje ilości zwycięstw dla strategii pozostania przy pierwszym wyborze można przeprowadzić w o wiele prostszy sposób. Można ją przeprowadzić bez stosowania pętli for! Zrób to w trzech krokach:

  • Wylosuj położenie nagrody dla 10000 kolejnych gier.
  • Wylosuj wskazania gracza dla 10000 kolejnych gier.
  • Sprawdź, ile było trafień.
bramka_z_nagroda = randi(3,10000,1);
wybor_gracza = randi(3,10000,1);
wygrana = bramka_z_nagroda == wybor_gracza;

Teraz wystarczy zsumować liczbę wygranych, podzielić przez 10000 i mamy prawdopodobieństwo wygranej przy wyborze pierwszej strategii.

sum(wygrana) / 10000

Dlaczego zatem stworzyliśmy dłuższy (i do tego wolniej działający) skrypt? No cóż, przede wszystkim chciałem Ci zademonstrować jak działa pętla for. Chciałem też aby to, co dzieje się w kodzie, jak najbardziej odpowiadało temu, co dzieje się w rzeczywistej grze. Warto jednak wyciągnąć z powyższego pewien wniosek: W MATLABie często można uniknąć stosowania pętli for. Osobom „skażonym” kontaktem z innymi językami programowania stosowanie pętli for będzie się często narzucać jako najbardziej naturalne ale pracując w MATLABie warto się dwa razy zastanowić. Unikając stosowania pętli często przyspieszamy obliczenia.

(Visited 398 times, 1 visits today)

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *