Matlab i teoria gier

advertisement
1
Spis treści
1. Podstawowe operacje w programie MATLAB ................................................................................ 2
2. M-pliki ............................................................................................................................................. 4
3. Macierze ........................................................................................................................................... 6
4. Wektory i funkcje ........................................................................................................................... 11
5. Instrukcje sterujące operacjami ...................................................................................................... 13
6. Grafika 2D...................................................................................................................................... 16
7. O teorii gier .................................................................................................................................... 21
8. Gry o sumie zero ............................................................................................................................ 21
9. Programowanie liniowe ................................................................................................................. 27
10. Gra o sumie zero jako problem programowania liniowego ......................................................... 33
11. Macierzowe gry niekooperacyjne i punkty siodłowe................................................................... 35
12. Rozwiązywanie gier niekooperacyjnych o małych wymiarach ................................................... 38
13. Dynamiczna (ewolucyjna) teoria gier .......................................................................................... 40
14. Okienka dialogowe ...................................................................................................................... 43
15. Podstawy animacji ....................................................................................................................... 45
16. Grafy w teorii gier – komponenty spójne .................................................................................... 47
17. Interaktywna współpraca z grafiką .............................................................................................. 50
18. Gry kombinatoryczne ................................................................................................................... 52
19. Gra Nim........................................................................................................................................ 54
2
1. Podstawowe operacje w programie MATLAB
MATLAB jest językiem programowania wysokiego poziomu wraz z
interaktywnym środowiskiem do wykonywania obliczeń i do tworzenia
symulacji komputerowych. Nazwa programu pochodzi od słów MATrix
LABoratory, ponieważ pierwsza wersja MATLABa została stworzona po
to by wspomóc numeryczne obliczenia macierzowe. Obecnie program ten
potrafi znacznie więcej, posiada dużą liczbę bibliotek i możliwości
rozbudowy przez programowanie własnych funkcji użytkownika. Jedna
cecha pozostała jednakże niezmienna. Typem danych jakim operuje
MATLAB jest macierz. Nawet liczba jest reprezentowana jako tablica o
wymiarach 1x1.
Dostępne operatory działań macierzowych:
 dodawanie +
 odejmowanie  mnożenie *
 dzielenie prawostronne / (dzielenie lewostronne \)
 potęgowanie ^
 transpozycja '
Jeśli zamierzamy wykonać działania mnożenia, dzielenia lub
potęgowania skalarnie to symbol działania musimy poprzedzić kropką !
Przykładowo: dla x = 1 3 5 polecenie: x.^2 daje wynik 1 9 25, natomiast
polecenie x^2 skutkuje wyłącznie wyświetleniem komunikatu o błędzie,
ponieważ niemożliwe jest podniesienie do kwadratu macierzy o
wymiarach 1x3.
Dostępne operatory logiczne:
 równe ==
 różne ~=
 większe >
 mniejsze <
 niewiększe <=
 niemniejsze >=
 i&
 lub |
3
Przydatne polecenia:
 clc - czyści okno terminala
 clear – usuwa zmienne z obszaru roboczego
 help komenda – wyświetla podstawowe informacje o komendzie
Dostępne stałe:
 pi - π = 3.1416
 i, j -  1 - jednostka urojona
 Inf - nieskończoność
 NaN - brak liczby (symbol nieoznaczony)
Zmienne w MATLAB-ie nie wymagają wcześniejszego deklarowania.
Domyślnym typem jest typ double. MATLAB używa zapisu liczb z
kropką jako separatorem dziesiętnym np. 5.872. Zakres wartości
bezwzględnej liczb wynosi od 2.2251 × 10−308 do 1.7977 × 10308 .
Sposób wyświetlania zależy od funkcji format.
Przykładowe formaty liczb:
 short – cztery miejsca po przecinku
 long – 15 miejsc po przecinku
 bank – dwa miejsca po przecinku
 rat – ułamki
Przykładowe użycie:
>> a = 0.12555;
>> format bank
>> a
a=
0.13
Ćwiczenie 1.1. Wypróbuj działanie różnych odmian funkcji format dla
1
zmiennej a = oraz zmiennej b = π.
3
Ćwiczenie 1.2. Wykonaj działanie dzielenia dwóch wybranych liczb
zespolonych. Zwróć uwagę na kolejność działań (użyj nawiasów).
4
2. M-pliki
Pliki programów MATLAB-a mają rozszerzenie .m (stąd nazwa M pliki). Domyślny folder do przechowywania plików użytkownika można
odczytać poleceniem pwd.
Tworząc własne programy w katalogu innym niż domyślny, trzeba
programowi wskazać ścieżkę dostępu. Można ją dodać komendą
addpath(nazwa_folderu) lub skorzystać z menu MATLAB-a. Żeby
utworzyć nowy plik wybieramy new z file menu lub open w celu edycji
istniejących plików. Nazwy plików powinny zaczynać się od litery,
program rozróżnia małe i duże litery. Wprowadzenie pliku o nazwie
identycznej z nazwą już istniejącego obiektu spowoduje jego przesłonięcie
tzn. z nazwą zostanie skojarzony ostatnio aktualizowany plik. Należy się
zatem wystrzegać nadawania plikom nazw tożsamych z nazwami poleceń
MATLAB-a.
M-pliki służą do tworzenia własnych poleceń i programów.
Wyróżniamy dwa typy m-plików: funkcje i skrypty. Typowa konstrukcja
funkcji (w naszym przypadku zależnej od dwóch zmiennych) wygląda
następująco:
function co_ma_być_wyświetlane_jako_wynik = nazwa_funkcji(a,b)
% znak po którym można umieścić komentarz
ciąg poleceń
Funkcja zawiera zestaw poleceń MATLAB-a i powinna zaczynać się
od słowa kluczowego function. Nazwa funkcji i pliku muszą być takie
same. Powyżej prezentowany przykład powinien zatem zostać zapisany na
dysku jako plik moja_funkcja.m. Zmienne a,b są parametrami
wejściowymi, a zmienna c jest parametrem wyjściowym. Parametry te nie
muszą być liczbami, ale np. macierzami czy ciągami znaków. Liczba
parametrów wejściowych i wyjściowych można być dowolna. Funkcja
przechowuje zmienne poza przestrzenią roboczą, można zatem dublować
nazwy zmiennych używanych przez inne pliki.
Funkcję wywołujemy wpisując jej nazwę wraz z danymi
wejściowymi w edytorze komend np. moja(2,5). Polecenia w funkcji mają
swoje echo. Jeśli nie chcemy oglądać wyniku danej operacji należy każde
takie polecenie zakończyć średnikiem.
5
Skrypt ma konstrukcję podobną do funkcji z tą różnicą, że nie
zaczyna się od słowa function. Skrypt w odróżnieniu od funkcji używa
zmiennych z przestrzeni roboczej. Poniższy skrypt po wywołaniu
wyświetla napis WITAM !
disp('WITAM !')
Skrypt w odróżnieniu od funkcji może być zapisany z dowolnie wybraną
nazwą.
Ćwiczenie 2.1. Zapisz skrypt wyświetlający liczbę pi w formacie long i
uruchom go. Wzbogać swój skrypt o instrukcję taką by po wywołaniu
wyświetlał się napis informujący użytkownika co robi dany skrypt.
Ćwiczenie 2.2. Napisz i wywołaj funkcję obliczającą sumę dwóch liczb.
6
3. Macierze
Najprostszym sposobem na utworzenie macierzy jest wypisanie jej
elementów w nawiasach kwadratowych. Elementy wprowadzamy
wpisując kolejne liczby we wierszach, a kolejne wiersze oddzielając
średnikami. Za separatory wyrazów we wierszach mogą służyć przecinki
lub odstępy. Przykładowa macierz:
>> a = [1 2 5; 3 4 3]
a=
1 2 5
3 4 3
Macierz można wywołać wpisując jej nazwę:
>> a
a=
1 2 5
3 4 3
Element macierzy w k-tym wierszu i n-tej kolumnie wywołujemy
poleceniem a(k,n). Program wyświetli wynik jako ans (od słowa answer).
>> a(1,3)
ans = 5
To samo, ale zapisane pod zmienną b:
>> b = a(1,3)
b=5
W podobny sposób można wywołać podmacierz danej macierzy np.
>> c = a(2,1:3) % lub c = a(2,1:end)
c=3 4 3
Wybraliśmy w ten sposób drugi wiersz i kolumny od 1 do 3.
7
Do generowania sekwencji używamy dwukropka np.
>> 2:6
ans =
2 3 4 5 6
Rozmiary macierzy mogą być powiększane przez łączenie np.
>> d = [a;[5 5 5]]
d=
1 2 5
3 4 3
5 5 5
>> d = [a [6;6]]
d=
1 2 5 6
3 4 3 6
przez wstawianie jako podmacierz np.
>> e = [0 0 0 0 0;0 0 0 0 0;0 0 0 0 0;0 0 0 0 0];
>> e(2:3,2:4) = a
e=
0 0 0 0 0
0 1 2 5 0
0 3 4 3 0
0 0 0 0 0
lub dodawanie nowego elementu spoza zakresu indeksów np.
>> a(3,3) = 6
a=
1 2 5 0
3 4 3 0
0 0 0 6
Skasować kolumnę lub wiersz można poprzez wpisanie w jego miejsce
pustej kolumny.
8
Usuwanie 3-ciej kolumny wygląda następująco:
>> a(:,3) = []
a=
1 2 0
3 4 0
0 0 6
Uwaga: pod a jest zapisana ostatnia macierz z tą nazwą !
Do poszukiwania elementów o żądnej własności służy komenda find.
Macierz jest przeszukiwana w sposób liniowy (przeszukiwane są kolejne
wyrazy w kolumnach od pierwszej do ostatniej).
>> find(a==4)
ans = 5
Jeśli zadeklarujemy chęć zobaczenia wyniku w postaci numerów wierszy i
kolumn wystarczy wpisać
>>[i,j]=find(a==4)
i=2
j=2
Przeszukiwanie zakończyło się znalezieniem liczby 4 na pozycji (2,2).
Do zamiany indeksu liniowego na numery wiersza i kolumn służy
polecenie ind2sub([liczba_wierszy liczba_kolumn],index_liniowy)
>> [i,j] = ind2sub([3 3],6) % lub [i,j] = ind2sub(size(a),6)
i=3
j=2
Jest to przydatne np. w celu znalezienia w macierzy pierwszego elementu
o zadanych własnościach (np. = 0). Do znalezienie minimum z indeksów
potrzebne jest indeksowanie liniowe, które później warto przetłumaczyć
na numery wiersza i kolumny. Jest to przydatne np. do konstruowania
solvera do sudoku.
9
Polecenie size(a) generuje wektor o długości 2 z liczbą wierszy i liczbą
kolumn macierzy a, size(a,1) daje liczbę wierszy, size(a,2) liczbę kolumn.
Polecenie (macierz warunek) daje w wyniku macierz o wartościach 1 w
miejscu, gdzie warunek jest spełniony i 0 jeśli nie jest spełniony. Wartości
1 i 0 są w tym wypadku traktowane jak wartości logiczne prawdy i fałszu.
Wywołajmy macierz a
a=
1 2 0
3 4 0
0 0 6
Wskażmy w których miejscach znajdują się liczby większe lub równe 3:
>> (a>=3)
ans =
0 0 0
1 1 0
0 0 1
Do zamiany macierzy na macierz wartości logicznych służy polecenie
logical. W wyniku otrzymujemy prawdę wszędzie tam, gdzie były
niezerowe elementy macierzy.
>> M = logical(a)
M=
1 1 0
1 1 0
0 0 1
Taka macierz może być używana jako maska:
>> b = [1 2 3;4 5 6;7 8 9]
b=
1 2 3
4 5 6
7 8 9
10
>> W = M.*b % mnożenie skalarne - wyraz po wyrazie
W=
1 2 0
4 5 0
0 0 9
Czasami konieczna jest zamiana macierzy na wektor i na odwrót:
>> h = [1 2;3 4];
>>f = h(:)
f=
1
3
2
4
>> g = reshape(f,3,3)
g=
1 2
3 4
MATLAB oferuje szereg funkcji do tworzenia macierzy specjalnych:
 zeros(n,m) – macierz wymiaru nxm składajaca się z samych zer
 ones(n,m) – macierz wymiaru nxm składajaca się z samych jedynek
 diag(wektor) – macierz diagonalna z elmentami wektora na
przekątnej
 eye(n) – macierz wymiaru nxn z jedynkami na przekątnej
 magic(n) – tzw. kwadrat magiczny o zadanym wymiarze
 rand(n,m) – macierz wymiaru nxm wypełniona wyrazami losowymi
z przedziału (0,1) (rozkład jednostajny)
Macierze są dwuwymiarowe. Pozycja każdego elementu w macierzy
opisana jest dwoma parametrami - numer wiersza i numer kolumny.
Tablice wielowymiarowe używają większej liczby indeksów. W poniższym
przykładzie jest zapisana macierz trójwymiarowa:
>>b(:,:,1) = [1 3; 2 4];
>>b(:,:,2) = [5 6; 7 1];
11
4. Wektory i funkcje
Wektory są szczególnym przypadkiem macierzy i wszystko o czym
napisano w poprzednim rozdziale ich dotyczy. Wektory generujemy za
pomocą wypisania ich elementów np.
>> x = [1 1.5 2 2.5 3];
lub za pomocą dwukropków:
>> x = 1:0.2:2
x = 1 1.2 1.4 1.6 1.8 2
Można też użyć polecenia linspace(s,t,n) by podzielić odcinek [s,t] na n-1
równych przedziałów i wypisać ich granice.
>> linspace(1,2,6)
x=
1 1.2 1.4 1.6 1.8 2
Dwie ostatnie metody są pomocne przy tworzeniu danych do rysowania
wykresów.
Dostęp do elementów wektora jest możliwy po podaniu numeru
współrzędnej:
>> x(3)
ans = 1.4
Współrzędne wektorów można sumować i mnożyć poleceniami sum i
prod, można też szukać elementów największych i najmniejszych
poleceniami max i min np.
>> max([1 4 2 5 2])
ans = 5
>> min([1 4 2 5 2])
ans = 1
12
Polecenia te stosowane do macierzy wykonują działania na kolejnych
kolumnach np.
>> a=[2 3 ;4 3]
a=
2 3
4 3
>> prod(a)
ans = 8 9
Na wektory nakładamy różne funkcje matematyczne. Podstawowe
funkcje zostały wypisane poniżej:
 abs – wartość bezwzględna
 sin, cos, tan, cot – funkcje trygonometryczne
 floor – zaokraglenie w dół (całość)
 ceil – zaokraglenie w górę
 exp – e do potęgi argumentu
 log – logarytm naturalny
 log10 – logarytm dziesiętny (analogicznie log2 – logarytm o
podstawie 2 itp.)
 sign – znak funkcji
 sqrt - pierwiastek kwadratowy
Ćwiczenie 4.1. Dla zadanej macierzy A=[1 3 6 3; 0 6 2 4; 1 7 0 8] napisz
polecenia:
 obliczające sumę wszystkich elementów macierzy,
 obliczające minimum z każdego wiersza,
 wyświetlające tylko te elementy macierzy, które są większe od 5
wraz z ich numerami wiersza i kolumny,
 generujące podmacierz złożoną tylko z dwóch ostatnich kolumn
macierzy A.
13
5. Instrukcje sterujące operacjami
Matlab umożliwia tworzenie własnych programów czyli plików o
rozszerzeniu .m (patrz rozdział o m-plikach). W takich funkcjach lub
skryptach można używać następujących instrukcji sterujących operacjami:
 if wyrażenie logiczne
instrukcje
elseif
instrukcje
else
instrukcje
end
 switch wyrażenie
case wyrażenie1
instrukcje
case wyrażenie2
instrukcje
otherwise
instrukcje
end
 for zmienna = początek:koniec
instrukcje
end
 while wyrażenie logiczne
instrukcje
end
14
Przykładowe m-pliki:
function b=znak(a)
% funkcja przyjmuje wartość 1 jeśli a>0, zero gdy a=0 i -1 dla a<0.
if a>0
b=1
elseif a==0
b=0
else
b=-1
end
function dzialania
%funkcja pozwala na wybranie działania (dodawanie lub odejmowanie) i
%wykonanie wybranej operacji na wpisanych liczbach
a = input('Podaj a: ');
b = input('Podaj b: ');
x = input('Wybierz dzialanie (+, -): ','s');
switch x
case '+'
a+b
case '-'
a–b
otherwise
disp('Nie wybrano dzialania lub liczb');
end
function s=sumowanie(n)
%funkcja wypisuje sumę liczb od 1 do n
s=0;
for k=1:n
s=s+k;
end
s
15
function ujemne
%funkcja wyświetla prosbe o wpisanie liczby do momentu wpisania liczby
%mniejszej lub równej zero.
w=1;
while w>0
w=input('Wpisz liczbe : ');
end
Ćwiczenie 5.1. Za pomocą poleceń MATLAB-a utwórz macierz o
wymiarach 3x4 taką, że wyraz o indeksie (i,j) tej nowej macierzy jest sumą
sąsiadów (we wszystkich możliwych kierunkach) wyrazu (i,j) macierzy
A = [1 3 2 3; 0 1 2 4; 1 1 0 2].
Ćwiczenie 5.2 Oblicz stałą e korzystając ze wzoru e =
∞
1
𝑛=0 n !
Wskazówka: Dla dużych n liczba 1/n! jest tak mała, że MATLAB traktuje
ją jako równą zero. Ilu wyrazów szeregu należy użyć ?
Ćwiczenie 5.3 Napisz funkcję, która wyświetla przybliżoną wartość x,
spełniającego równanie 𝑥 = x 21+1 iterując formułę 𝑥𝑛+1 = 21 ,
x n +1
rozpoczynając od 𝑥1 = 1 i wykonując tyle kroków by dwa ostatnie
wyrazy spełniały warunek 𝑥𝑘+1 − 𝑥𝑘 ≤ 0.0001.
16
6. Grafika 2D
Najczęściej spotykanym sposobem graficznej prezentacji danych w
języku MATLAB jest wykres funkcji jednej zmiennej. Służy do tego
funkcja plot(x,y). Jeśli y jest wektorem, plot(y) utworzy wykres funkcji
liniowej, gdzie na jednej osi będą numery współrzędnych, a na drugiej
elementy wektora y. Jeżeli jako argumenty funkcji plot podamy dwa
wektory x i y, komenda plot(x , y) utworzy wykres wektora y względem
wektora x.
Na przykład, aby wykreślić wartość funkcji sinus od
napisać
x = 0:pi/100:2*pi;
y = sin(x);
plot(x,y)
Otrzymujemy następujący obrazek:
0
do
2π,
możemy
17
Podanie wielu par x-y umożliwia utworzenie wielu wykresów przy
jednym wywołaniu funkcji plot. MATLAB automatycznie dobiera kolor
dla każdego zestawu danych. Na przykład, poniższy zapis kreśli trzy
funkcje, każdą w innym, wyróżniającym się kolorze.
x = 0:pi/100:2*pi;
y1= sin(x);
y2 = sin(2*x);
y3 = cos(x);
plot(t,y,t,y2,t,y3)
Poszczególnym liniom na wykresie można nadać różne dodatkowe
właściwości np:
 rodzaj linii – parametr 'LineStyle' np. '-', '--', ':'
 kolor linii – parametr 'Color' np 'y' – żółty, 'm' – fioletowy,
'c' – turkusowy, 'r' – czerwony, 'g' – zielony, 'b' – niebieski,
'w' – biały, 'k' – czarny.
 grubość linii – parametr 'LineWidth' – grubość linii w punktach
 rodzaj znaczników – parametr 'Marker' np. '+', 'o', 'p', 'd' , 'x', '<' itp.
 kolor krawędzi znacznika – parametr 'MarkerEdgeColor'
 kolor wypełnienia znacznika – parametr 'MarkerFaceColor'
 wielkość znaczników – parametr 'MarkerSize', wartość jest
podawana w punktach.
Skala wykresu jest dobierana automatycznie. Chcąc ją zmienić trzeba
wywołać funkcję axis([xmin xmax ymin ymax]) i jako argument podać
wektor określający nowe parametry osi.
Do opisu wykresu służą polecenia:
- title(„tekst‟) – dodaje tytuł rysunku;
- xlabel(„tekst‟) – dodaje opis osi x;
- ylabel(„tekst‟) – dodaje opis osi y;
- text(x,y,„tekst‟) - umieszcza „tekst‟ w punkcie o współrzędnych (x,y);
- legend('tekst1','tekst2',....) - tworzy legendę.
Polecenie grid on/off – włącza/wyłącza siatkę, hold on/off
włącza/wyłącza tryb zachowania zawartości okna (przydatne gdy chcemy
narysować nieznaną z góry liczbę wykresów w jednym oknie).
18
W poniższym przykładzie \leq oznacza <=, \pi oznacza symbol liczby pi.
t = -pi:pi/100:pi;
y = sin(t);
plot(t,y)
axis([-pi pi -1 1])
xlabel('-\pi \leq \pi')
ylabel('sin(t)')
title('wykres funkcji sinus')
text(1,-1/3,'\it{Funkcja nieparzysta.}')
Przydatne polecenia:
• Okno graficzne można wyczyścić wywołując funkcję clf;
• Nowe okna można otworzyć przy pomocy funkcji figure;
• W celu podzielenia okna graficznego na mniejsze okienka należy
wykorzystać funkcję subplot(m,n,p), gdzie: m - liczba okienek w pionie; n
- liczba okienek w poziomie; p - kolejny numer wykresu.
• Otworzyć okno można podając jego numer jako argument;
19
W Matlabie istnieje system specyfikacji kolorów. Jest on oparty na
kombinacji czerwonego, zielonego i niebieskiego, każdy kolor jest
rozpatrywany jako kombinacja tych trzech z odpowiednią intensywnością.
W tym systemie kolor jest identyfikowany w wektorem trójwymiarowym,
gdzie każda współrzędna odpowiada za intensywność kolejno koloru
czerwonego, zielonego i niebieskiego. Intensywności mierzymy liczbami z
zakresu od 0 do 1. Wektor [0 0 0] koresponduje z kolorem czarnym, a
wektor [1 1 1] z kolorem białym.
Mapę kolorów można zadać poleceniem jet(liczba kolorów) lub
hsv(liczba). Są także dostępne palety kolorów (np. gray, summer, autumn,
spring, winter, hot, cool itp.). Aktualną paletę kolorów zmienia polecenie
colormap(nazwa_palety) lub colormap(macierz_palety).
Poniższa funkcja na jednym układzie współrzędnych rysuje wykresy
funkcji 𝑦𝑘 = sin(kx) dla k z podanego zakresu.
function wykres(n),
x=[0:0.01:4];
kolor = jet(n);
for k = [1:n],
plot(x,sin(k*x),'Color', kolor(k,:)),
hold on
end
Poniższy skrypt generuje „kolorowy kwadrat”:
a=[0 100;30 50];
image(a)
colormap(hsv(16))
Oprócz wykresów MATLAB rysuje też wielokąty, a co za tym idzie potrafi
też zaznaczać obszary między wykresami. Służy do tego komenda fill np.
x=[0:0.01:pi];
X=[x,fliplr(x)]; % fliplr(x) odwraca kolejność współrzędnych w wektorze
Y=[sin(x), fliplr(cos(x))];
fill(X,Y,'r')
20
Ćwiczenie 6.1 Napisz program, który po otrzymaniu liczby n rysuje w
różnych kolorach n parabol o współczynniku kierunkowym zmieniającym
się w zakresie 1 do n. Przetestuj różne palety kolorów.
Ćwiczenie 6.2 Narysuj pierwszą literę swojego nazwiska. Następnie
pomniejsz ją, przesuń oraz obróć. Umieść na jednym rysunku obie litery,
każdą w odmiennym kolorze. Zrób „szlaczek” powtarzając wielokrotnie
podobne procedury.
Wskazówka: Utwórz wektor W=[x;y;ones(length(x))]. Do zmiany
rozmiaru służy przekształcenie skala=[s 0 0; 0 s 0; 0 0 1], gdzie s jest stałą
jednokładności. Obrót jest zadany przez macierz: obrot=[cos(a) -sin(a) 0;
sin(a) cos(a) 0; 0 0 1], gdzie a jest kątem o jaki chcemy obrócić literę.
Natomiast do przesuwania służy przekształcenie przes=[1 0 t1; 0 1 t2; 0 0
1].
Ćwiczenie 6.3 Narysuj liść paproci wg algorytmu:
1. Ustal liczbę punktów i liczbę iteracji.
2. Utwórz macierz zerową przygotowaną na wpisanie w pierwszym
wierszu pierwszej współrzędnej punktów, a w drugim drugiej
współrzędnej.
3. Wylosuj współrzędne punktów z przedziału (0,1)
4. W pętli losuj wartość z przedziału (0,1). W zależności od tej wartości
wybierz przekształcenie 1, 2, 3 lub 4 i wylicz nowe współrzędne
punktów (do następnej iteracji).
5. Narysuj liść paproci z punktów o współrzędnych wyliczonych po
ostatniej iteracji.
Przekształcenia:
Nr 1 : x=0, y=0.16y, częstotliwość 1%
Nr 2 : x=0.2x-0.26y, y=0.23x + 0.22y+1.6, częstotliwość 7 %
Nr 3 : x=-0.15x +0.28y, y=0.26x+0.24y+0.44, częstotliwość 7 %
Nr 4 : x= 0.85x+0.04y, y=-0.04x+0.85y+1.6, częstotliwość 85 %.
Pokombinuj z liczbą punktów na starcie, liczbą iteracji, odcieniami zieleni
paprotki i proporcjami częstotliwości różnych przekształceń.
21
7. O teorii gier
Teoria gier modeluje sytuacje konfliktowe, w których występuje
wielu uczestników. Każdy z uczestników ma zestaw dostępnych decyzji do
podjęcia oraz reguł wedle jakich te decyzje ma podejmować. W zależności
od podjętych decyzji każdy z graczy otrzymuje wypłatę. Zakłada się, że
wszyscy uczestnicy gry zachowują się racjonalnie, to znaczy, że każdy
stara się maksymalnie zwiększyć swoją wypłatę. Teoria gier dostarcza
narzędzi do doradzania graczom jak wybierać optymalne strategie w grze
wedle określonych preferencji. Działa ona również w sytuacji kiedy mamy
do czynienia z jednym graczem, działającym w warunkach niepewności
lub przy niepełnych danych. Mówiąc w skrócie, teoria gier jest teorią
podejmowania decyzji. Teoria gier znalazła zastosowanie w ekonomii,
biologii ewolucyjnej, socjologii, naukach politycznych i informatyce. Gry
dzielimy na jedno i wieloosobowe, równoczesne i naprzemienne,
pojedyncze i iterowane, z pełną i niepełną informacją, kooperacyjne i
niekooperacyjne oraz kombinatoryczne. Omawianie gier zaczniemy od
najprostszego modelu gier niekooperacyjnych o sumie zero.
8. Gry o sumie zero
W tym rozdziale opiszemy gry macierzowe o sumie zero. Nazwa gry
bierze się faktu, że wypłaty graczy sumują się do zera (jeden gracz płaci
drugiemu). Gra taka z konstrukcji jest zatem dwuosobowa i zdecydowanie
nie zachęca do współpracy konkurujących graczy. Jest to zatem gra
niekooperacyjna.
Gra tego typu zawiera zazwyczaj następujące elementy:
 gracze - I i II;
 strategie - dla gracza I-szego X={1,2,3...,n}, dla gracza II-go
Y={1,2,3...,m};
 macierz wypłat A wymiaru n x m. Wyraz a(i,j) oznacza wypłatę
pierwszego gracza, w sytuacji gdy gracz pierwszy podjął decyzję 'i',
a gracz drugi podjął decyzję 'j'. Decyzje gracza I odpowiadają zatem
wyborowi wierszy, a gracza II kolumn w macierzy A.
22
Dopuszcza się stosowanie tzw. strategii mieszanych, które są
rozkładami prawdopodobieństwa kolejno na X i na Y. Odpowiadają one
zachowaniu dużych grup graczy w miejsce pojedynczego gracza.
Prawdopodobieństwa wyboru poszczególnych decyzji są określone przez
procent graczy w grupie podejmujących daną decyzję. Gra polega na tym,
że każdy osobnik z grupy pierwszej rozgrywa grę z każdym osobnikiem z
grupy drugiej.
Jeśli p = (𝑝1 , ⋯ , 𝑝𝑛 ) jest wektorem prawdopodobieństwa gracza I, a
q = (𝑞1 , ⋯ , 𝑞𝑚 ) jest wektorem prawdopodobieństwa gracza II to wypłatę
dla gracza I liczymy ze wzoru:
𝑢1 𝑝, 𝑞 = 𝑝𝐴𝑞 𝑇 ,
a gracza II-go
𝑢2 𝑝, 𝑞 = −𝑝𝐴𝑞 𝑇 .
Strategia gracza pierwszego oznaczona jako wybór 'i' odpowiada w
tym ujęciu wektorowi bazowemu 𝑒𝑖 = (0,0, ⋯ ,1, ⋯ ,0), gdzie jedynka
stoi na i-tym miejscu . Taką strategię nazywamy czystą i piszemy 𝑝 = 𝑒𝑖 .
Definicja (dolna i górna wartość gry)
Dla gry o macierzy wypłat A=(a(i,j)) liczba 𝑣 = min𝑗 ( max𝑖 𝑎(𝑖, 𝑗)) jest
nazywana górną, a liczba 𝑣 = max𝑖 ( min𝑗 𝑎(𝑖, 𝑗)) dolną wartością gry.
Definicja (rozwiązanie gry)
Rozwiązaniem gry nazywamy wektory prawdopodobieństwa
𝑝∗ = (𝑝1 , ⋯ , 𝑝𝑛 ) i 𝑞 ∗ = (𝑞1 , ⋯ , 𝑞𝑚 ) oraz liczbę rzeczywistą v takie, że
spełnione są warunki 𝑢1 (𝑝∗ , 𝑞 ∗ ) = 𝑣 i 𝑢2 𝑝∗ , 𝑞 ∗ = −𝑣 . Strategie 𝑝∗ , 𝑞 ∗
nazywamy optymalnymi, a liczbę v nazywamy wartością gry.
Twierdzenie (von Neumann, Morgenstern) (twierdzenie minimaksowe,
zasadnicze twierdzenie teorii gier o sumie zero)
Każda gra macierzowa o sumie zero ma przynajmniej jedno rozwiązanie.
Każdemu z rozwiązań odpowiada ta sama wartości gry v. Jest ona równa
𝑣 = max( min 𝑢1 (𝑝, 𝑞)) = min( max 𝑢1 (𝑝, 𝑞))
𝑝
𝑞
𝑞
𝑝
Jeśli 𝑣 = 𝑣 to taką grę nazywamy grą zamkniętą, strategie optymalne są
wtedy zadane przez strategie czyste, a wartość gry jest równa 𝑣 = 𝑣 = 𝑣.
23
Definicja (punkt siodłowy)
Punkt siodłowy to ten z elementów macierzy A który wyznacza dolną i
górną wartość gry w przypadku gry zamkniętej.
6 3 5
punkt siodłowy jest
4 1 3
jeden i jest to wyraz w pierwszym wierszu i w drugiej kolumnie,
zaznaczony kolorem czerwonym. Wartość gry wynosi 3. Słowo siodłowy
bierze się stąd, że takiej liczbie we wierszu towarzyszą same wyrazy
większe lub równe, a w kolumnie mniejsze lub równe. Żadnemu z graczy
nie opłaca się zatem zrezygnować z tak obranej strategii.
Dla gry o macierzy wypłat 𝐴 =
Bardziej problematyczne jest rozwiązywanie gier otwartych (takich
dla których dolna i górna wartość gry nie są równe). Jeśli macierz wypłat
na dwa wiersze lub dwie kolumny z pomocą przychodzi nam algorytm
rozwiązania graficznego.
2 3 4
. Strategię gracza pierwszego
5 2 1
można zadać przez liczbę x z przedziału [0,1] oznaczającą
prawdopodobieństwo wyboru pierwszego wiersza. Wtedy wypłaty gracza I
w zależności od kolumny wybranej przez gracza II są funkcjami liniowymi
od x. Można je nanieść na układ współrzędnych. Gracz II wybierze tzw.
obwiednię dolną zadanych prostych, bo zgodnie z swoimi preferencjami w
ten sposób będzie minimalizować swoją stratę (przypomnienie: wypłata
gracza I = strata gracza II). Gracz I wybierze taka wartość x, która na
obwiedni odpowiada największej wartości. Prosta 𝑘1 jest postaci
𝑘1 : y=2x+5(1-x)=5-3x (2 i 5 to elementy w pierszej kolumnie),
prosta 𝑘2 : y=3x+2(1-x), a 𝑘3 : y=4x+(1-x). Wartość gry v odpowiada
maksimum obwiedni dolnej, a strategia optymalna gracza I wektorowi
(xopt,1-xopt) dla xopt realizujacego to maksimum.
Rozważmy macierz 𝐴 =
24
Można to zobrazować graficznie:
Funkcja realizująca powyższe zadanie:
function gra2_n(a), %funkcja od macierzy z dwoma wierszami
m=size(a,2); % liczba kolumn
x=[0:0.01:1]; % zakres x
kol=jet(m); % paleta kolorów
for k=[1:m], % powtarzaj od k=1 do k=m
plot(x,a(1,k)*x+a(2,k)*(1-x),'Color', kol(k,:),'Linewidth',2)
xlabel('strategia gracza I', „fontsize‟, 12), % podpis osi x
ylabel('wartosc gry', „fontsize‟,12), %podpis osi y
hold on,
end
f=a(1,1)*x+a(2,1)*(1-x);
for k=[2:m],
f=min(a(1,k)*x+a(2,k)*(1-x),f);
end
25
plot(x,f,'b*')
Żeby obliczyć strategię gracza II trzeba wybrać dwie kolumny,
których wykresy przecinają się w punkcie (xopt,v), czyli w tym przypadku
kolumna nr 1 i 2 i rozwiązać układ równań 2y+3(1-y)=5y+2(1-y).
Używane elementy zostały wyróżnione w macierzy A
2
5
3
2
4
1
Sytuacja komplikuje się nieco jeśli przez punkt (xopt, v) przechodzą
wykresy więcej niż dwóch prostych odpowiadających kolumnom. Wtedy
trzeba rozpatrzeć każdą parę takich kolumn z osobna i wyliczyć
odpowiadającą jej strategię gracza II. Optymalna dla gry będzie każda
strategia, która jest kombinacją wypukłą w ten sposób wyliczonych
strategii.
Analogicznie postępujemy z macierzami o dwóch kolumnach.
Różnica polega na rysowaniu wykresów zależności od y, gdzie y jest
prawdopodobieństwem wyboru pierwszej kolumny i szukaniu minimum
obwiedni górnej (zamiast maksimum obwiedni dolnej).
Ćwiczenie 8.1 Wzbogać zaprezentowany program o obliczanie strategii
optymalnych i wartość gry. Przydatne będzie polecenie fzero.
Przykładowe użycie:
>> fzero(@(x) sin(x) , [-1,1]), % szuka miejsca zerowego funkcji
>> ans = 0
Ćwiczenie 8.2 Napisz program rozwiązujący gry z macierzą wypłat o
dwóch kolumnach. Wybierz inne znaczniki do wyeksponowania obwiedni
górnej niż zastosowane w prezentowanym programie.
26
Ćwiczenie 8.3 Wypróbuj ciąg poleceń:
repmat('<',1,10)
sprintf('f%i',1:10)
s=6;
disp(sprintf('liczba %d',s))
disp('liczba s')
k=1.5;
disp(sprintf('wynik wynosi %f',k))
disp('wynik wynosi k')
i=12;
fprintf('wykonano %d krokow', i);
Opisz funkcje poleceń sprintf, fprintf i repmat.
Ćwiczenie 8.4 Napisz program do rysowania macierzy. Wynik działania
dla macierzy A=[2 3 7 0; 1 5 2 5] może wyglądać następująco:
2
3
1
5
7
2
0
5
Udoskonal program tak, by zaznaczał punkty siodłowe, jeśli takie istnieją.
Do zmiany wielkości czcionki służy parametr 'Fontsize', którego wartość
podaje się w punktach np.
>> k = 45
>> text(-1,9, sprintf('%d',k), 'Fontsize',22))
27
9. Programowanie liniowe
Ogólne sformułowanie problemu programowania liniowego jest
następujące:
zmaksymalizować (lub zminimalizować)
𝑛
𝑧 = 𝑓 𝑥1 , ⋯ , 𝑥𝑛 =
𝑐𝑗 𝑥𝑗
𝑗 =1
przy ograniczeniach
𝑛
𝑎𝑖𝑗 𝑥𝑗 ≤ 𝑏𝑖 , 𝑖 = 1, … , 𝑚.
𝑗 =1
Współczynniki 𝑎𝑖𝑗 , 𝑏𝑖 , 𝑐𝑗 są zadanymi parametrami, a 𝑥𝑗 są zmiennymi
decyzyjnymi. Zakłada się, że wszystkie zmienne decyzyjne są nieujemne.
Istnieje kilka metod rozwiązywania tego typu zadań. Najprostszą jest
tzw. metoda graficzna, ale jej zastosowanie ogranicza się do przykładów z
dwoma zmiennymi decyzyjnymi. drugą jest metoda simplex. Nazwa tej
metody pochodzi od symplexu czyli wypukłej figury wielowymiarowej,
która zadaje zbiór dopuszczalnych punktów 𝑥 = (𝑥1 , ⋯ , 𝑥𝑛 ) spełniających
ograniczenia problemu. Jest to metoda, która w większości przypadków
znajduje rozwiązanie w stosunkowo krótkim czasie. Do chwili obecnej
zaproponowano kilka innych, asymptotycznie wydajniejszych algorytmów
np. metoda elipsoidalna czy metoda punktu wewnętrznego, ale ich
przewaga nad metodą simplex ujawnia się dopiero przy zmiennych i
ograniczeniach liczonych w tysiącach. W roku 2000 uznano algorytm
simplex za jeden z 10 czołowych algorytmów ubiegłego wieku.
Do rozwiązywania problemów programowania liniowego służy
wbudowana funkcja MATLAB-a linprog. Można także użyć funkcji
simplex z poniżej zapisanych plików (wykorzystywane podfunkcje + plik
funkcji głównej).
28
function e = wr(m,i)
% i-ty wektor bazowy w przestrzeni R^m
e = zeros(m,1);
e(i) = 1;
function [row, mi] = MRT(a, b)
% Test minimalnego ilorazu (minimum ratio test) na wektorach a i b.
% row – numer wiersza (pivot row)
% mi – wartość najmniejszego ilorazu (minimum)
m = length(a);
c = 1:m;
a = a(:);
b = b(:);
l = c(b > 0);
[mi, row] = min(a(l)./b(l));
row = l(row);
function [m, j] = Br(d)
% Reguła Branda zastosowana do macierzy d.
% Wynik:
% m - pierwsza ujemna liczba w macierzy d
% j - index (indexowanie liniowe) liczby m.
ind = find(d < 0);
if ~isempty(ind)
j = ind(1);
m = d(j);
else
m = [];
j = [];
end
function [subs, A, z]= petla_simplex(A, subs, mm, k)
%Pętla do algorytmu simplex
% A macierz problemu
%mm=0 jeśli min, a mm=1 jeśli max.
[m, n] = size(A);
[mi, col] = Br(A(m,1:n-1));
29
while ~isempty(mi) & mi<0 & abs(mi)>eps
[row, small] = MRT(A(1:m-k,n),A(1:m-k,col));
if ~isempty(row)
if abs(small) <= 100*eps & k == 1
[s,col] = Br(A(m,1:n-1));
end
A(row,:)= A(row,:)/A(row,col);
subs(row) = col;
for i = 1:m
if i ~= row
A(i,:)= A(i,:)-A(i,col)*A(row,:);
end
end
[mi, col] = Br(A(m,1:n-1));
end
end
z = A(m,n);
function z=simplex(typ, c, A, rel, b)
% typ - min(or max) z = c*x
% Pod warunkami Ax rel b, x >= 0 !!!
% Parametr typ: 'min' lub 'max'.
% Parametr rel jest znakiem określającym znaki nierowności:
% np rel = '<=>' oznacza nierówność <, rowność i jedną nierówność >
% A jest macierzą
% b jest wektorem kolumnowym
% c jest wektorem
if (typ == 'min')
mm = 0;
else
mm = 1;
c = -c;
end
b = b(:);
c = c(:)';
[m, n] = size(A);
n1 = n;
30
les = 0;
neq = 0;
red = 0;
if length(c) < n
c = [c zeros(1,n-length(c))];
end
for i=1:m
if (rel(i) == '<')
A = [A wr(m,i)];
les = les + 1;
elseif (rel(i) == '>')
A = [A -wr(m,i)];
else
neq = neq + 1;
end
end
ncol = length(A);
if les == m
c = [c zeros(1,ncol-length(c))];
A = [A;c];
A = [A [b;0]];
[subs, A, z] = petla_simplex(A, n1+1:ncol, mm, 1);
else
A = [A eye(m) b];
if m > 1
w = -sum(A(1:m,1:ncol));
else
w = -A(1,1:ncol);
end
c = [c zeros(1,length(A)-length(c))];
A = [A;c];
A = [A;[w zeros(1,m) -sum(b)]];
subs = ncol+1:ncol+m;
av = subs;
[subs, A, z] = petla_simplex(A, subs, mm, 2);
nc = ncol + m + 1;
31
x = zeros(nc-1,1);
x(subs) = A(1:m,nc);
xa = x(av);
com = intersect(subs,av);
if (any(xa) ~= 0)
disp(sprintf(' Pusty obszar, zignoruj wynik funkcji'))
return
else
if ~isempty(com)
red = 1;
end
end
A = A(1:m+1,1:nc);
A =[A(1:m+1,1:ncol) A(1:m+1,nc)];
[subs, A, z] = petla_simplex(A, subs, mm, 1);
end
if (z == inf | z == -inf)
return
end
[m, n] = size(A);
x = zeros(n,1);
x(subs) = A(1:m-1,n);
x = x(1:n1);
if mm == 0
z = -A(m,n);
else
z = A(m,n);
end
%disp(sprintf('Problem ma rozwiazanie '))
%disp(sprintf(' Wartosci zmiennych:'))
%for i=1:n1
%disp(sprintf(' x(%d)= %f ',i,x(i)))
%end
%disp(sprintf(' Optymalna wartosc funkcji celu:'))
%disp(sprintf(' z= %f',z))
32
Przykład użycia: Zmaksymalizuj funkcję z = F(x,y) = 30x+20y
przy ograniczeniach 2x+y ≤ 1000, 3x+3y ≤ 2400, 1.5 x ≤ 600, x, y ≥ 0.
>> type = 'max';
>> c = [30 20];
>> A = [2 1;3 3;1.5 0];
>> b = [1000;2400;600];
>> rel = '<<<';
>> z = simplex(type, c, A, rel, b)
>> z=18000 ← to jest wynik
Ćwiczenie 9.1 Wykonaj ilustrację graficzną do przykładu prezentowanego
powyżej. Zaprojektuj program rysujący obszar punktów (x,y)
spełniających zadane ograniczenia, na ten obszar nanieś wykres funkcji
30x+20y = a, gdzie a jest wyliczonym przez Matlab maximum funkcji
F(x,y) = 30x+20y na narysowanym obszarze.
33
10. Gra o sumie zero jako problem programowania
liniowego
Programowanie liniowe jest metodą na rozwiązywanie gier dowolnej
wielkości. Rozważmy grę o sumie zero z następującą macierzą wypłat:
0
𝐴 = −1
2
1
0
−3
−2
3
0
Szukamy minimalnej wartości v, że
𝑣 ≥ 0 ∙ 𝑦1 + 1 ∙ 𝑦2 −2 ∙ 𝑦3 ,
𝑣 ≥ −1 ∙ 𝑦1 + 0 ∙ 𝑦2 + 3 ∙ 𝑦3 ,
𝑣 ≥ 2 ∙ 𝑦1 − 3 ∙ 𝑦2 + 0 ∙ 𝑦3 ,
pod warunkami
𝑦𝑖 ≥ 0 i 𝑦1 + 𝑦2 + 𝑦3 = 1.
Przenosząc wartość 𝑣 na prawą stronę powyższych równań
otrzymujemy problem programowania liniowego polegający na
zminimalizowaniu wartości v (𝑏𝑖 = 0 dla każdego 𝑖).
W problemie programowania liniowego występuje założenie o
nieujemności zmiennych. Wartość gry czasami bywa jednak ujemna. Tą
przeszkodę można ominąć dodając do każdego wyrazu macierzy A stałą
liczbę p, tak aby wszystkie wyrazy nowej macierzy wypłat były
nieujemne. Wartość tej nowej gry wynosi według teorii gier p + v, gdzie v
jest wartością wyjściowej gry i jest to liczba nieujemna.
Program liczący wartość naszej przekładowej gry wygląda zatem
następująco:
type = 'min';
a = [0 1 -2; -1 0 3; 2 -3 0];
mma = min(min(a));
a1 = a-mma*ones(3,3); % tak żeby wartość gry była dodatnia
c1 = zeros(1,3);
c = [c1 1];
A1 = [a1 -ones(3,1)];
34
A = vertcat(A1,[ones(1,3) 0]);
r = repmat('<',1,3);
rel = '<<<='; % lub rel=strcat(r,'=') % strcat – łączenie znaków;
Pr = vertcat(zeros(3,1),[1]);
z = simplex(type, c, A, rel, pr);
v = z+mma
Wywołanie programu daje wynik v=0.
Ćwiczenie 10.1 Prześledź krok po kroku i wyjaśnij jakie operacje
wykonuje powyżej prezentowany program.
Ćwiczenie 10.2 Przerób podany skrypt na funkcję liczącą wartość gry dla
dowolnej gry o sumie zero.
Ćwiczenie 10.3 Niech G będzie grafem skierowanym z wierzchołkami ze
zbioru W. Gracz pierwszy wybiera wierzchołki, a drugi krawędzie. Jeśli
wybrana krawędź nie zaczyna się ani nie kończy w wybranym
wierzchołku, to wypłata wynosi 0, jeśli zaczyna się w wybranym
wierzchołku, to wypłata wynosi 1, a jeśli kończy to -1. Napisz macierz gry
dla wybranego przez siebie grafu (przynajmniej trzy wierzchołki i
przynajmniej trzy krawędzie). Oblicz wartość takiej gry. Pokaż, że w
każdym przypadku wartość gry jest nieujemna. Wskazówka: jaką strategię
gwarantująca brak straty (zerową wypłatę) może przyjąć gracz I ?
35
11. Macierzowe gry niekooperacyjne i punkty siodłowe
W poprzednich rozdziałach zakładaliśmy, że suma wypłat graczy
wynosi zero. W ogólnym modelu macierzowych gier niekooperacyjnych
wygrana jednego z graczy nie musi być automatycznie przegraną
drugiego, a suma wypłat graczy może zależeć od ich strategii. Interesy
graczy nie są zatem całkowicie przeciwstawne. Gracze mogą
maksymalizować jednocześnie różne wielkości. Możliwe jest też
wprowadzenie większej liczby graczy. Konstrukcja takiej gry jest podobna
do konstrukcji gry o sumie zero z ta różnicą, że każdy z graczy będzie
korzystał ze swojej macierzy wypłat.
Podstawowe elementy gry to:
 gracze,
 strategie graczy,
 wielowymiarowe macierze wypłat.
Dla trzech graczy dane są w tym przypadku trzy trójwymiarowe
macierze wypłat takich samych rozmiarów a,b i c. Wyraz a(i,j,k) zadaje
wypłatę pierwszego gracza, w sytuacji gdy gracz pierwszy podjął decyzję
'i', a gracz drugi podjął decyzję 'j', a trzeci decyzję 'k'. Analogicznie w
macierzy b są zapisane wypłaty gracza II, a w macierzy c, wypłaty gracza
III. Decyzje gracza I odpowiadają wyborowi wierszy, gracza II kolumn, a
gracza III warstw w odpowiednich macierzach. Podobnie jak w grach o
sumie zero rozpatrujemy strategie mieszane, bo tylko takie ujęcie zbioru
strategii gwarantuje znalezienie rozwiązania gry. Na początku będziemy
rozważać gry dwuosobowe, ale wszystkie wprowadzone pojęcia bez trudu
uogólniają się na gry wieloosobowe.
Definicja (funkcje wypłat)
Jeśli A jest macierzą wypłat gracza I, a B macierzą wypłat gracza II oraz
𝑝 = (𝑝1 , ⋯ , 𝑝𝑛 ) jest wektorem prawdopodobieństwa gracza I, a
𝑞 = (𝑞1 , ⋯ , 𝑞𝑛 ) jest wektorem prawdopodobieństwa gracza II to wypłatę
dla gracza I liczymy ze wzoru 𝑢1 𝑝, 𝑞 = 𝑝 ∙ 𝐴 ∙ 𝑞 𝑇 , a gracza II
𝑢2 𝑝, 𝑞 = 𝑝 ∙ 𝐵 ∙ 𝑞 𝑇 .
36
Definicja (Rozwiązanie gry, punkt Nasha)
Rozwiązaniem gry (punktem Nasha) nazywamy każdą parę strategii
(p*, q*) taką, że 𝑢1 𝑝∗ , 𝑞 ∗ ≥ 𝑢1 (𝑝, 𝑞 ∗ ) oraz 𝑢2 𝑝∗ , 𝑞 ∗ ≥ 𝑢2 (𝑝∗ , 𝑞) dla
dowolnych strategii p i q.
Oznacza to, że dowolna zmiana strategii jednego z graczy, (bez
zmiany strategii drugiego) nie spowoduje wzrostu jego wygranej (czyli
krótko mówiąc jest nieopłacalna).
Twierdzenie (Nash)
Każda gra macierzowa ma przynajmniej jeden punkt równowagi.
Punktów Nasha może być wiele, co gorsza każdemu z nich może
odpowiadać inna para wypłat (czyli nie ma w tak skonstruowanych grach
pojęcia wartości gry). W jednym z rozwiązań wypłaty mogą być
korzystniejsze dla jednego z graczy, a w innym dla drugiego. Kluczowym
problemem staje się zatem nie tylko wyliczenie punktów Nasha, ale ich
odpowiednia selekcja.
Sztandarowym przykładem gry dwuosobowej nie o sumie zero jest
dylemat więźnia. Dwóch osaczonych w areszcie jest przesłuchiwanych na
okoliczność wspólnie dokonanego przestępstwa. Obaj mężczyźni są
przesłuchiwani oddzielnie i mają do dyspozycji następujące możliwości:
przyznać się do winy (zdradzić), nie przyznawać się (milczeć). Jeśli obaj
osaczeni przyznają się do winy zostają skazani na 5 lat. Jeśli jeden z nich
przyzna się, a drugi milczy to ten co się przyznał wychodzi na wolność, a
drugi dostaje karę 10 lat więzienia. Jeśli obaj milczą wyjdą na wolność po
odsiedzeniu roku więzienia. Jest to tzw. gra symetryczna czy sytuacja obu
graczy jest z ich punktu widzenia identyczna. Ustalmy, że przez A
będziemy oznaczać macierz wypłat gracza I, a przez B gracza II.
W tym wypadku
𝐴=
−1
0
−10
−5
i 𝐵 = 𝐴𝑇 ,
gdzie strategia wyboru pierwszego wiersza odpowiada za milczenie, a
drugiego za zdradę (analogicznie dla kolumn w przypadku gracza II).
Z łatwością można zauważyć, że w przypadku braku komunikacji między
graczami preferują oni zdradę, choć najlepszym rozwiązaniem byłoby
zachowanie przez obu milczenia. Stąd nazwa gry: dylemat więźnia.
37
Para strategii (zdradź, zdradź) jest jednym punktem Nasha w grze.
Można powiedzieć, że jest to taki uogólniony punkt siodłowy.
Procedura szukania punktów siodłowych w ogólnej grze
niekooperacyjnej jest inna niż w przypadku gier o sumie zero ze względu
na to, że mamy do dyspozycji dwie macierze. Najlepiej wyjaśnić ją na
przykładzie.
Przykład
7 6 3
2 7 6
i 𝐵=
.
2 3 4
7 2 5
Gracz pierwszy stara się zmaksymalizować swoją wypłatę w związku z
czym szuka wyrazów maksymalnych w każdej kolumnie macierzy A, a
gracz drugi szuka wyrazów maksymalnych, ale we wierszach macierzy B.
Odpowiednie wyrazy zostały pogrubione poniżej:
Weźmy grę o macierzach wypłat 𝐴 =
𝐴=
𝟕
2
𝟔 3
3 𝟒
𝐵=
2
𝟕
𝟕 6
2 5
W pierwszym wierszu i drugiej kolumnie widoczna jest zgodność intencji
graczy. Jest to nasz szukany uogólniony punkt siodłowy.
Do szukania pojedynczych punktów Nasha w grach
niekooperacyjnych (ale nie o sumie zero) służy algorytm Lemke –
Howson. Jest to algorytm o strukturze podobnej do programowania
liniowego. Szukanie wszystkich punktów Nasha jest pracochłonne. Jak
wcześniej wspomnieliśmy kluczowym problemem jest wybór
odpowiednich punktów równowagi. W związku z tym w naszym skrypcie
zajmiemy się szczególnymi typami gier w przypadku których znalezienie
punktów Nasha jest łatwe lub szukamy punktów o z góry określonych
właściwościach.
Ćwiczenie 11.1 Skonstruuj program do rysowania dwóch macierzy i
zaznaczania punktów siodłowych.
38
12. Rozwiązywanie gier niekooperacyjnych o małych
wymiarach
Definicja
Zbiorem racjonalnych reakcji gracza I, który oznaczamy przez 𝑅1
nazywamy zbiór wszystkich par strategii (p, q) takich, że p jest najlepszą
odpowiedzią na q, gdzie q przebiega zbiór wszystkich strategii gracza II
tzn. 𝑝, 𝑞 ∈ 𝑅1 dokładnie wtedy gdy 𝑢1 𝑝, 𝑞 = max𝑟 𝑢1 (𝑟, 𝑞).
Analogicznie definiujemy zbiór 𝑅2 .
Fakt
Zbiór punktów Nasha jest częścią wspólną zbiorów racjonalnych reakcji.
W przypadku gier 2x2 strategie graczy można zadać poprzez podanie
dwóch liczb: x z przedziału [0,1] – prawdopodobieństwo użycia
pierwszego wiersza przez gracza I i y z przedziału [0,1] –
prawdopodobieństwo użycia pierwszej kolumny przez gracza II. W
związku z tym zbiory racjonalnych reakcji można przedstawić jako punkty
na płaszczyźnie i w ten sposób bez trudu znaleźć wszystkie punkty Nasha.
Takie rozwiązanie nazywamy graficznym. Przykładowy rysunek dla
macierzy A=[2 3;4 1] i B=[1 5;4 1] wygląda następująco:
39
Punkty Nasha zapisane jako pary (x;y) to (1;0), (0;1) oraz (3/7; 1/2).
Oznacza to w pierwszym przypadku wybranie pierwszego wiersza i
drugiej kolumny, w drugim drugiego wiersza i pierwszej kolumny, a w
trzecim wybór pierwszego wiersza z prawdopodobieństwem 3/7 a
drugiego z prawdopodobieństwem 2/7, ponadto wybór pierwszej i drugiej
kolumny z prawdopodobieństwami 1/2.
Ćwiczenie 12.1 Wykonaj graficzną reprezentację zbiorów racjonalnych
reakcji dla gry o macierzach wypłat: A=[6 3;3 4] i B=[2 7; 7 2].
40
13. Dynamiczna (ewolucyjna) teoria gier
W ewolucyjnej teorii gier rozważamy gry symetryczne dotyczące
populacji różnego typu osobników. Zakłada się, że zdolność reprodukcyjna
jest proporcjonalna do średniej wypłaty dla danego typu osobników.
Proces zmiany liczebności w kolejnych pokoleniach (powtórzeniach gry)
nazywamy dynamiką replikacyjną. Odbywa się ona według wzoru:
𝑝𝑡+1 𝑠 = 𝑝𝑡 (𝑠)
𝐴𝑠 𝑝 𝑇
𝑝𝐴𝑝 𝑇
,
gdzie s jest numerem typu osobnika 𝐴𝑠 oznacza s-ty wiersz w macierzy
wypłat symetrycznej gry macierzowej.
Przypomnienie: W symetrycznej grze macierzowej macierz wypłat
drugiego gracza jest macierzą transponowaną do macierzy wypłat gracza
pierwszego. W opisie gry wystarczy zatem podać macierz wypłat
pierwszego gracza, zazwyczaj oznaczoną przez A.
W zależności od stanu początkowego dynamika replikacyjna
prowadzi do konstrukcji tzw. punktów ewolucyjnie stabilnych. Maynard
Smith jako pierwszy wprowadził to pojęcie jako propozycję równowagi w
dynamicznej teorii gier.
Definicja
Mówimy, że strategia p (proporcje określonych osobników w liczbach
sumujących się do jedności) jest strategią ewolucyjnie stabilną (ESS) jeśli
(p,p) jest punktem Nasha oraz „żaden mutant korzystający z innej strategii
nie ma szans dokonania inwazji na populację”. Warunek drugi w języku
matematyki oznacza, że dla strategii r „niewiele różniącej się” od p mamy
𝑢1 𝑝, 𝑟 ≥ 𝑢1 (𝑟, 𝑟) (populacja w starych proporcjach ma przynajmniej
taki sam potencjał jak nowych w zmienionym przez inwazję środowisku).
Przykład
Rozważmy populację złożoną z trzech typów osobników: uczciwych,
którzy zawsze chcą połowę zasobów, zachłannych, którzy zawsze chcą
więcej niż połowę np. 2/3 i skromnych, którzy zadowalają się 1/3. Kiedy
jeden zachłanny spotyka drugiego zachłannego to obydwaj nic nie dostają
ze względu na walkę o zasoby. Jeśli zachłanny spotyka uczciwego obaj też
41
nic nie otrzymują. Tylko spotkanie: zachłanny – skromny skutkuje
podziałem zasobów w proporcji 2/3 i 1/3. Dwaj uczciwi podzielą się
zasobami po połowie, dwaj skromni wezmą po 1/3, a spotkanie skromnego
z uczciwym da rezultat 1/3 dla skromnego i 1/2 dla uczciwego.
Przyjmując, że typ uczciwy oznaczymy jako typ 1, skromny jako typ 2, a
zachłanny jako typ 3, wypłaty w naszej symetrycznej grze można
przedstawić za pomocą macierzy (w celu uniknięcia wpisywania ułamków
wypłaty zostały pomnożone przez 6, nie ma to wpływu na strategie
optymalne):
3
𝐴= 2
0
3
2
4
0
2
0
Istnieją dwa ewolucyjnie stabilne punkty w takiej grze. Jednym z nich jest
strategia wyboru pierwszego wiersza czyli stan w którym populacja składa
się wyłącznie z osobników uczciwych. Nawet bez zastosowania wzorów
matematycznych łatwo zauważyć, że gdyby w tej populacji pojawił się
osobnik zachłanny to jego wypłata byłaby równa zero, co nie daje mu
szans na przeżycie (zachłanni do przeżycia potrzebują pewnego odsetka
skromnych), a jeśli pojawi się osobnik skromny, dostanie wypłatę
mniejszą niż uczciwy i w związku z tym także nie ma dużych szans na
propagowanie swojej strategii. Znalezienie drugiego punktu ewolucyjnie
stabilnego pozostawiam jako ćwiczenie.
Ćwiczenie 13.1 Napisz program, który po podaniu początkowej liczby
osobników każdego typu przeprowadzi populację z podanego przykładu
zgodnie z dynamiką replikacyjną przez 50 pokoleń.
Ćwiczenie 13.2 Funkcja Matlaba [X , Y] = meshgrid(x, y) przekształca
wektory x i y w parę macierzy X i Y. Wiersze wynikowej macierzy X są
kopiami wektora x, kolumny wynikowej macierzy Y są kopiami wektora y.
Taka „chmura” punktów może być użyta do znajdowania wartości lub
testowania własności funkcji dwóch zmiennych. Wypróbuj ciąg poleceń:
>> x = 1:0.5:3 ;
>> y = 21:25;
>> [X,Y] = meshgrid(x,y)
42
Ćwiczenie 13.3 Utwórz “chmurę” punktów będących wierzchołkami
podziału kwadratu o boku 1 na kwadraty o boku 1/10. Narysuj punkty
poleceniem scatter(x,y). Jaki wynik da zastosowanie polecenia plot(x,y) ?
Wyjaśnij dlaczego tak to wygląda ?
Ćwiczenie 13.4 Określ obszary przyciągania dla każdego z punktów
ewolucyjnie stabilnych z poprzedniego ćwiczenia według następującego
algorytmu:
1. Zadaj „chmurę” punktów dzieląc zakres x i y na przedziały długości
1/100.
2. Jeśli x+y<=1 to przyjmij x za procent osobników uczciwych, a y
skromnych.
3. Ustal do jakiego punktu zbliżają się strategie zadane dynamiką
ewolucyjną po np.100 iteracjach (ustal zakres błędu).
4. Za pomocą polecenia find znajdź, które punkty zbliżyły się
dostatecznie blisko strategii „sami uczciwi” i używając polecenia
plot zaznacz je na wykresie na czerwono. Pozostałe punkty z
„chmury” spełniające warunek x+y<=1 zaznacz na niebiesko.
5. Uzupełnij rysunek odpowiednią legendą.
Ćwiczenie 13.5 Znajdź w internecie opis gry jastrząb - gołąb i oblicz
strategie ewolucyjnie stabilne. Odnajdź informacje na czym polegają
strategie mściciela i pozera. Przeprowadź symulacje dla tak rozbudowanej
gry 4x4.
43
14. Okienka dialogowe
Niedogodnością w prowadzeniu symulacji jest konieczność
każdorazowego wpisywania parametrów w programie i wywoływania
nazwy funkcji w głównym oknie programu MATLAB. Dużym
ułatwieniem jest wyświetlanie przez program zachęty do wpisania danych
lub okienka dialogowe. Pierwsza opcja jest dostępna na Octave
(darmowym odpowiedniku Matlaba), druga jest dostępna tylko dla
użytkowników MATLAB-a.
Wysłać na ekran zachętę do wpisania danych można poleceniem
input
Jeśli chcemy by program zapisał dane w postaci ciągu znaków trzeba to
zaznaczyć. Polecenie w = input('Wpisz liczbe: ') (uwaga na polskie znaki
!) po wpisaniu 4 nada zmiennej „w” wartość 4. Polecenie w = input('Wpisz
macierz: ' ,'s') zapisze zmienną w jako ciąg znaków (nawiasów i symboli
liczbowych np. w=[2 3;4 1]). Ciąg znaków na polecenia MATLAB-a (w
podanym przykładzie na macierz) zamienia komenda eval. Ciągi znaków
można porównać poleceniem strcmp.
Ćwiczenie 14.1 Napisz i wywołaj skrypt. Wyjaśnij jego działanie krok po
kroku. Czego nie zobaczymy na ekranie po wstawieniu średnika w trzeciej
i przedostatniej linijce skryptu ?
zapyt='tak';
while strcmp(zapyt, 'tak')
wzor=input('podaj macierz: ','s')
s=eval(wzor)
if size(s,2)>1
disp('duzo kolumn')
else
disp('malo kolumn')
end
zapyt=input('czy chcesz wykonac program po raz kolejny ? Wpisz tak lub
nie: ','s')
end
44
Okienka dialogowe to proste interfejsy do wprowadzania danych.
Korzystamy w tym wypadku z polecenia inputdlg
Przykład tworzenia okna dialogowego do programu rysującego
minimum lub maksimum z dwóch funkcji na przedziale [-2,2]
function maxmin
% napisy przy polach do wprowadzania zmiennych
prompt={'y1:', 'y2:','Max czy min ? Napisz max lub min'};
tutul='Wzory funkcji'; % nazwa okna
linie=1; %liczba linii do wpisania
def={'cos(x)','2*x','max'}; % wartości domyślne
answer=inputdlg(prompt,tutul,linie,def);
x=[-2:0.01:2];
y1=eval(answer{1});
y2=eval(answer{2});
if strcmp{answer{3},'min'} % jeśli minimum
y=min(y1,y2);
elseif strcmp{answer{3},'max'} % jeśli maksimum
y=max(y1,y2);
end
plot(x,y1,x,y2,x,y)
Ćwiczenie 14.2 Napisz program wyświetlający okienko dialogowe z
obrazka oraz wyświetlające kolorowy pasek z kolorami z palety hsv(n) lub
jet(n), gdzie n jest wybrana liczbą.
Ćwiczenie 14.3 Wzbogać program do symulacji z poprzedniego rozdziału
(ćwiczenie 15.1) o okienko dialogowe do wprowadzania liczby graczy
poszczególnych typów.
45
15. Podstawy animacji
Obserwację przebiegu symulacji można przeprowadzać w czasie
rzeczywistym w postaci animacji. W tym rozdziale omówimy potrzebne
do tego celu narzędzia.
Matlab umożliwia bardzo łatwe tworzenie animacji wykresów.
Zawartość okna graficznego można zapisać przy pomocy polecenia
getframe zapisać np.: w tablicy. Utworzoną tablicę odtworzymy
poleceniem movie. Poniższy krótki skrypt ukazuje działanie
poleceń:
x=0:0.01:5;
for k = 1:16
plot(x,sin(k*x))
M(k) = getframe;
end
movie(M,20) % liczba powtórzeń
%movie(M,20,4) % ostatni argument to liczba obrazków na sekundę,
%domyślna wartość to 12.
Ćwiczenie 15.1 Zrób animację w postaci obracającej się litery (patrz
rozdział 8) .
Innym sposobem na tworzenie animacji jak również zrobienia pokazu
slajdów jest zastosowanie polecenia pause. Zatrzymuje ono wykonywanie
instrukcji do chwili naciśnięcia przycisku z klawiatury. Polecenie pause(n)
zatrzymuje program na n sekund (n może być mniejsze od 1 np. 0.1).
Poniższy skrypt pokazuje jak zrobić znikający napis
tempo = 0.1 : 0.02: 1;
for k = tempo
text(0,0,'Napis na srodku', 'Fontsize', 22,'Color',[k k k]);
axis([-1 2 -1 1])
pause(0.05);
end;
46
Trzecią metodą na tworzenie animacji jest obserwacja procesów w czasie
rzeczywistym tzn. program aktualizuje obiekty graficzne pobierając
kolejny gotowy rysunek. Służy temu polecenie drawnow. Polecenie to,
podobnie jak poprzednie, stosowane jest głównie do rysowania wykresów
w pętli.
Poniższy skrypt „rysuje” sinosoidę w przedziale [0,10]
x=linspace(0:10,1000);
for k=1:1000
x1=x(1:k);
plot(x1,sin(x1)) % bardzo szybko
axis([0,10,0,2])
drawnow
end
Ćwiczenie 15.2 Przerób program tak by na pierwszym wykresie widniała
gotowa sinusoida, a następne znikała kawałek po kawałku z upływem
czasu.
47
16. Grafy w teorii gier – komponenty spójne
W tym rozdziale wyjaśnię jak szukać komponent spójnych grafu oraz
jak sąsiedztwo punktów przetłumaczyć na język teorii grafów. Graf jest
strukturą składająca się z wierzchołków V i krawędzi E. Elementami
zbioru E są uporządkowane pary punktów ze zbioru wierzchołków. Na
potrzeby tego skryptu będziemy zajmować się grafami nieskierowanymi.
Grafy można opisywać za pomocą podania zbioru liczb, który numeruje
wierzchołki i zbioru par tych liczb dla których odpowiadające im
wierzchołki są połączone. Dane na temat grafu można także zapisać za
pomocą macierzy sąsiedztwa A. Jest to macierz kwadratowa wymiaru n,
gdzie n jest liczbą wierzchołków. Elementami tej macierzy są 0 i 1. Jeśli
wierzchołki 'i' i 'j' są połączone to wyraz A(i,j)=1. W pozostałych
przypadkach A(i,j)=0.
Dla macierzy sąsiedztwa A
A=[0
1
1
0
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
0
0
0
1
1
0
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
1
0
0
1
1
0
0
0
0
0
0
1
1
0
0
1
0
0
0
0
0
0
1
0
0
1
0;...
0;...
0;...
0;...
0;...
0;...
0;...
1;...
1;...
0]
graf wygląda następująco:
48
Rysunek uzyskano posługując się komendą gplot(A,xy), gdzie
xy=[0 0;0 1;1 3;2 3;4 2;4 1;3 -1;3 -2;2 -2;1 -1] jest spisem współrzędnych
kolejnych wierzchołków.
Komponenta spójna składa się z tych wszystkich wierzchołków,
które są bezpośrednio lub pośrednio ze sobą połączone tzn. istnieje droga
(ciąg krawędzi) w grafie, która je łączy. Nasz przykładowy graf ma dwie
komponenty spójne. Do wyliczania komponent spójnych służy polecenie
[p,q,r,s]=dmperm(B), gdzie B=A+Id (Id macierz z jedynkami na głównej
przekątnej, macierz identycznościowa). Interesują nas zmienne p i r.
Wektor p jest permutacją numerów wierzchołków, a wektor r podaje
numer początku kolejnej komponenty spójnej po permutacji p. Dokładniej
p(r(1):r(2)-1) daje spis numerów wierzchołków z pierwszej komponenty
spójnej, p(r(2): r(3)-1) drugiej komponenty spójnej itd. Liczba komponent
spójnych to o jeden mniej niż liczba współrzędnych wektora r .
49
Ćwiczenie 16.1 Wypróbuj i zinterpretuj:
function graf
A=[0 1 1 0 0 0 0 0 0 0;1 0 0 1 0 0 0 0 0 0;...
1 0 0 1 0 0 0 0 0 0;0 1 1 0 0 0 0 0 0 0;...
0 0 0 0 0 1 1 0 0 0;0 0 0 0 1 0 0 1 0 0;...
0 0 0 0 1 0 0 1 1 0;0 0 0 0 0 1 1 0 0 1;...
0 0 0 0 0 0 1 0 0 1;0 0 0 0 0 0 0 1 1 0];
xy=[0 0;0 1;1 3;2 3;4 2;4 1;3 -1;3 -2;2 -2;1 -1];
gplot(A,xy)
[p,q,r,s]=dmperm(A+eye(10));
p
r
Ćwiczenie 16.2 Narysuj dwa rozłączne trójkąty. Ponumeruj wierzchołki
tak aby kolene numery zostały rozmieszczone na dwóch różnych
trójkątach. Napisz macierz sąsiedztwa tak skonstruowanego grafu.
Zastosuj i zinterpretuj polecenia z poprzedniego ćwiczenia.
Ćwiczenie 16.3 Napisz program, który po podaniu wektora liczb
naturalnych z przedziału [1,10] wybiera z grafu z ćwiczenia 23.1 tylko
wierzchołki o podanych numerach, sporządza rysunek takiego grafu, a
następnie podaje czy graf jest spójny czy nie.
Ćwiczenie 16.4 Stwórz procedurę rysująca grafy w 3d dla podanej
macierzy (na początek może by dla ustalonego wymiaru).
50
17. Interaktywna współpraca z grafiką
Do współpracy z grafiką potrzebny jest dostęp do jej parametrów.
Każdy obiekt jest identyfikowany przez liczbę. Tę liczbę
przyporządkowuje się zmiennej w momencie tworzenia grafiki np.
h=plot(x,sin(x)). Do każdego obiektu można się odwołać podając jej
identyfikator (tzw. uchwyt), w naszym przykładzie jest to litera h, lub
bezpośrednio podając liczbę. Odczytać własności obiektu można używając
funkcji get np. get(h) (obiekt w otwartym oknie), wartość żądanej
własności można pobrać używając polecenia get(id_obiektu,'nazwa
własności'), zmienić parametry można za pomocą funkcji
set(id_obiektu,'nazwa własności', nowa wartość).
Ważne uchwyty:
 gca (get current axes) – zwraca uchwyt do aktywnego układu
współrzędnych
 gcbo (get callback object) – zwraca uchwyt do obiektu, z którego
wywołano Callback
 gcf (get current figure) - zwraca uchwyt aktywnego okna
 gcbf (get callback figure) – zwraca uchwyt do okna z którego
wywołano Callback
Współpracę z grafiką umożliwia własność ButtonDownFcn. Poniższa
funkcja prezentuje przykład obsługi tej własności.
function wspolpraca
x=[0:0.01:8];
y=sin(x);
h=plot(x,y,'linewidth', 2)
set(gca, 'ButtonDownFcn', 'obsluga_gca')
set(h, 'ButtonDownFcn', 'obsluga_wykres')
function obsluga_gca
text(1,0.5,'klik')
% t=text(...), pause(3), set(t,'Color',[1 1 1]) lub set(t,'Visible', 'off')
% pobieranie współrzędnych myszki (dwie współrzędne)
punkt=get(gca,'CurrrentPoint')
51
function obsluga_wykres
grubosc=get(gcbo,'linewidth');
set(gcbo,'linewidth', 6 - grubosc)
Ćwiczenie 17.1 Utwórz program wyświetlający napis 'klik' w miejscu
kliknięcia.
Ćwiczenie 17.2 Utwórz program, który na kliknięcie w okno graficzne
rysuje w tym miejscu punkt o zadanych właściwościach. Do rysowania
punktu można użyć polecenia line(współrzędna1, współrzędna2,'marker',
'rodzaj markera', 'markersize', liczba).
Jak wysłać współrzędne położenia myszki w określone miejsce ?
Służy do tego polecenie assignin. Wysłać informacje można albo do
innego programu albo do głównej przestrzeni roboczej. Wypróbuj funkcję
function zapisz
x=5;
% eksmisja do głównej przestrzeni roboczej
assignin('base', 'nowyx', x);
% eksmisja do programu który wywołuje funkcję zapisz
assignin('caller', 'nowyx', x);
Ćwiczenie 17.3 Udoskonal grę „15” tak by ruchy wykonywać za pomocą
myszki na rysunku tablicy gry.
52
18. Gry kombinatoryczne
Definicja Gry kombinatoryczne to gry dwuosobowe z pełną
informacją, bez czynnika losowego, z zero-jedynkowymi wypłatami.
Gracze wykonują ruchy naprzemiennie aż do zakończenia gry.
Gry kombinatoryczne dzielimy na bezstronne (zbiór dostępnych
ruchów dla gracza zależy wyłącznie od pozycji w grze) i wielostronne
(dostępność ruchów zależy np. od koloru pionka gracza).
Elementy skończonej gry bezstronnej:
 dwóch graczy,
 skończony zbiór pozycji w grze,
 skończony zestaw ruchów dostępnych w każdej pozycji.
Zasady skończonej gry bezstronnej:
 gracze wykonują ruchy naprzemiennie
 gra kończy się gdy nie ma już możliwości ruchu
 gracz, który wykona ostatni ruch wygrywa.
Założenie dodatkowe: gra kończy się zawsze po skończonej liczbie
ruchów.
Przykład (gra odejmowania) Pozycją początkowa w grze jest liczba 21.
Ruch w grze polega na odjęciu 1,2 lub 3 od rezultatu poprzedniego gracza.
W każdej bezstronnej, skończonej grze kombinatorycznej pozycje
możemy podzielić na wygrywające (oznaczone przez W) i przegrywające
(oznaczone przez P). Każda pozycja otrzymuje etykietkę W albo P.
Pozycja przegrywająca to taka z której każdy ruch prowadzi do pozycji
wygrywającej, a wygrywająca to taka z której istnieje co najmniej jeden
ruch prowadzący do pozycji przegrywającej.
Dla naszej przykładowej gry odejmowania pozycje przegrywające to
wielokrotności liczby 4. Jedną z metod nadawania odpowiednich etykietek
pozycjom w grze jest metoda indukcji wstecznej. Polega ona na
przeprowadzenia analizy zaczynając od końcowych etapów gry.
53
Ćwiczenie 18.1 Znajdź pozycje przegrywające w grze odejmowania, w
której od wyniku przeciwnika można odjąć co najwyżej połowę. Jakie są
pozycje końcowe w tej grze ? Czy gracz rozpoczynający grę może ją
wygrać, jeśli pozycją początkową jest liczba 100 ? Przeprowadź metodą
indukcji wstecznej.
Inne przykłady gier
1. Grę zaczynamy od ustawienia dwóch stosów (n i m krążków
odpowiednio na każdym ze stosów). Ruch w grze polega na
usunięciu jednego ze stosów i podzieleniu drugiego na dwa w
dowolnej proporcji.
2. Zaczynamy grę od wybranej liczby (np. 44). Gracz rozpoczynający
może od niej odjąć dowolną liczbę od niej mniejszą. Kolejne ruchy
graczy polegają na odjęciu od wyniku uzyskanego przez przeciwnika
nie więcej niż odjął przeciwnik w poprzednim ruchu.
3. Gra SOS – grę zaczynamy mając do dyspozycji 7 pustych
kwadratów ustawionych sąsiadująco w jednym rzędzie. Gracze
naprzemiennie wpisują po jednej literze w dowolnie wybrany pusty
kwadrat (sami wybierają kwadrat i literę S lub O). Wygrywa gracz,
któremu jako pierwszemu uda się napisać słowo SOS. Jeśli ta sztuka
nie uda się żadnemu z graczy przyjmujemy to za remis.
54
19. Gra Nim
Najbardziej znaną grą odejmowania jest gra Nim. Wybieramy liczbę
naturalną k (k>1) i wektor liczb naturalnych 𝑥 = (𝑥1 , ⋯ , 𝑥𝑘 ). Rozważamy
k stosów, j-ty stos składa się z 𝑥𝑗 elementów. Ruch gracza polega na
wybraniu jednego ze stosów i zabraniu z niego dowolnej liczby
elementów. Wygrywa ten gracz, który jako ostatni ma możliwość ruchu.
Każdą pozycję w tej grze można zakodować jako k elementowy wektor.
Jedyną pozycją końcową jest pozycja samych zer. Zgodnie z nomenklaturą
wprowadzoną w poprzednim rozdziale pozycje w grze dzielimy na
wygrywające (oznaczone przez W) i przegrywające (oznaczone przez P).
W przypadku k=2 pozycje przegrywające to wektory o dwóch, równych
współrzędnych. Jeśli mamy do czynienia z trzema niepustymi stosami,
rozstrzygnięcie, które pozycje są wygrywające (i co więcej jak wykonać
optymalne posunięcie z takiej pozycji) jest bardziej skomplikowane.
Można skorzystać z indukcji wstecznej, ale ta metoda jest bardzo
nieefektywna jeśli chodzi o złożoność obliczeniową.
Definicja Sumą nim dwóch liczb naturalnych a i b oznaczoną przez
a NIM b nazywamy liczbę, której kolejne cyfry w zapisie dwójkowym są
sumą cyfr w zapisie dwójkowym a i b modulo 2.
Jak to działa ?
Każdą liczbę można zapisać jako 𝑎 = 𝑎𝑚 2𝑚 + 𝑎𝑚 −1 2𝑚 −1 +. . . +𝑎0 dla
pewnego m, gdzie każde 𝑎𝑗 jest zerem lub jedynką. Będziemy w takim
wypadku używać notacji 𝑎 = 𝑎𝑚 , 𝑎𝑚 −1 , . . . , 𝑎0 2 . Chcąc dodać liczby a i
b każdą z nich trzeba zapisać w notacji dwójkowej tej samej długości.
Definicja Suma nim liczb a i b dla 𝑎 = 𝑎𝑚 , 𝑎𝑚 −1 , . . . , 𝑎0 2 i
𝑏 = 𝑏𝑚 , 𝑏𝑚 −1 , . . . , 𝑏0 2 jest liczbą 𝑐 = 𝑐𝑚 , 𝑐𝑚 −1 , . . . , 𝑐0 2 taką, że
𝑐𝑗 = 𝑎𝑗 + 𝑏𝑗 𝑚𝑜𝑑2 czyli 𝑐𝑗 = 0 dokładnie wtedy gdy 𝑎𝑗 = 𝑏𝑗 .
Przykład: Obliczmy 20 NIM 5. Zaczynamy od zapisania obu składowych
liczb w systemie dwójkowym czyli 20= 10100 2 , a 5= 00101 2 .
Liczby 20 i 5 mają obie jedynkę na trzecim miejscu od końca zatem ich
suma nim będzie miała na tym miejscu (w swoim zapisie) cyfrę zero.
55
𝑎
𝑏
𝑐
1
0
1
0
0
0
1
1
0
0
0
0
0
1 czyli 𝑐= 10001 2 =17 .
1
Ostatecznie 20 NIM 5 = 17.
Twierdzenie Pozycja 𝑥 = 𝑥1, 𝑥2,... , 𝑥𝑘 jest przegrywająca wtedy i tylko
wtedy gdy suma nim współrzędnych wektora x wynosi 0.
Dowód polega na wskazaniu metody konstrukcji ruchu z pozycji
wygrywającej do przegrywającej i pokazaniu, że każdy ruch z pozycji
przegrywającej prowadzi do pozycji wygrywającej.
Ćwiczenie 19.1 Ustal czy w grze nim z trzema stosami o liczbie
elementów równej kolejno 12,15 i 20. Uzasadnij, że jest to pozycja
wygrywająca i wskaż optymalny ruch w gracza rozpoczynającego grę.
Download