11 Commits

4 changed files with 136 additions and 105 deletions

View File

@@ -1,4 +1,4 @@
import System.Random (randomRIO)
import System.Random (StdGen, mkStdGen, randomR)
type Point = (Float, Float)
@@ -21,22 +21,20 @@ applyTransformation point random
| random < 0.93 = transformation3 point
| otherwise = transformation4 point
genNextPoint :: Point -> IO Point
genNextPoint point = do
random <- randomRIO (0.0, 1.0 :: Float)
return $ applyTransformation point random
genNextPoint :: StdGen -> Point -> (Point, StdGen)
genNextPoint gen point =
let (random, newGen) = randomR (0.0, 1.0 :: Float) gen
in (applyTransformation point random, newGen)
barnsleyFern :: Point -> Int -> IO [Point]
barnsleyFern _ 0 = return []
barnsleyFern startPoint n = do
x' <- genNextPoint startPoint
xs <- barnsleyFern x' (n - 1)
return (startPoint : xs)
barnsleyFern :: StdGen -> Point -> Int -> [Point]
barnsleyFern _ _ 0 = []
barnsleyFern gen startPoint n =
let (nextPoint, newGen) = genNextPoint gen startPoint
in startPoint : barnsleyFern newGen nextPoint (n - 1)
n = 1000
randomSeed = 17
main :: IO ()
main = do
putStrLn "Укажите количество шагов рекурсии:"
input <- getLine
let n = read input :: Int
fractal <- barnsleyFern (0, 0) n
print fractal
main = print $ barnsleyFern (mkStdGen randomSeed) (0, 0) n

View File

@@ -0,0 +1,44 @@
-- Не стоит в первой же лабе использовать монады, можно и без них
import System.Random (randomRIO)
type Point = (Float, Float)
transformation1 :: Point -> Point
transformation1 (_, y) = (0, 0.16 * y)
transformation2 :: Point -> Point
transformation2 (x, y) = (0.85 * x + 0.04 * y, -0.04 * x + 0.85 * y + 1.6)
transformation3 :: Point -> Point
transformation3 (x, y) = (0.2 * x - 0.26 * y, 0.23 * x + 0.22 * y + 1.6)
transformation4 :: Point -> Point
transformation4 (x, y) = (-0.15 * x + 0.28 * y, 0.26 * x + 0.24 * y + 0.44)
applyTransformation :: Point -> Float -> Point
applyTransformation point random
| random < 0.01 = transformation1 point
| random < 0.86 = transformation2 point
| random < 0.93 = transformation3 point
| otherwise = transformation4 point
genNextPoint :: Point -> IO Point
genNextPoint point = do
random <- randomRIO (0.0, 1.0 :: Float)
return $ applyTransformation point random
barnsleyFern :: Point -> Int -> IO [Point]
barnsleyFern _ 0 = return []
barnsleyFern startPoint n = do
x' <- genNextPoint startPoint
xs <- barnsleyFern x' (n - 1)
return (startPoint : xs)
main :: IO ()
main = do
putStrLn "Укажите количество шагов рекурсии:"
input <- getLine
let n = read input :: Int
fractal <- barnsleyFern (0, 0) n
print fractal

View File

@@ -21,13 +21,12 @@ nashEquilibriumStrategy opponentMoves generatedMoves n =
nashEquilibriumStrategy (tail opponentMoves) (generatedMoves ++ [nextStep]) (n + 1)
else generatedMoves
where
cases = [['П', 'С'], ['П', 'П']]
results = [[0, 10], [5, 5]]
result =
[ min (results !! 0 !! 1) (results !! 1 !! 1),
max (results !! 0 !! 0) (results !! 1 !! 0)
]
nextStep = cases !! indexOf results result !! 1
cases = [['С', 'С'], ['С', 'П'], ['П', 'С'], ['П', 'П']]
results = [[1, 1], [10, 0], [0, 10], [5, 5]]
p_years = min (results !! 1 !! 1) (results !! 3 !! 1)
s_years = min (results !! 0 !! 1) (results !! 2 !! 1)
nextStep | p_years <= s_years = 'П'
| otherwise = 'С'
getScore :: [Char] -> [Char] -> Int -> Int -> (Int, Int)
getScore moves1 moves2 score1 score2 =
@@ -47,7 +46,8 @@ game playerMoves gameStrategy
playerMoves = ['С', 'П', 'С', 'С', 'П', 'П', 'С', 'С', 'П', 'П']
gameStrategy = forgivingStrategy -- forgivingStrategy, nashEquilibriumStrategy
-- gameStrategy = forgivingStrategy
gameStrategy = nashEquilibriumStrategy
computerMoves = game playerMoves gameStrategy
(score1, score2) = getScore playerMoves computerMoves 0 0

View File

@@ -103,7 +103,7 @@
extendedchars=\true,
inputencoding=utf8,
keepspaces=true,
captionpos=b,
captionpos=t,
}
\begin{document} % начало документа
@@ -187,29 +187,30 @@
P(T_1) = 0.01, \quad P(T_2) = 0.85, \quad P(T_3) = 0.07, \quad P(T_4) = 0.07.
\]
\begin{figure}[h!]
\centering
\includegraphics[width=0.31\linewidth]{img/fern500.png}
\caption{Папоротник Барнсли для n = 500.}
\label{fig:fern500}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.31\linewidth]{img/fern5000.png}
\caption{Папоротник Барнсли для n = 5000.}
\label{fig:fern5000}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.31\linewidth]{img/fern50000.png}
\caption{Папоротник Барнсли для n = 50000.}
\label{fig:fern50000}
\end{figure}
На рисунках 1-3 приведены примеры папоротника Барнсли для разного количества точек (n). Во всех примерах в качестве начальной была выбрана точка с координатами (0, 0).
\begin{figure}[h!]
\centering
\includegraphics[width=0.41\linewidth]{img/fern500.png}
\caption{Папоротник Барнсли для n = 500.}
\label{fig:fern500}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.41\linewidth]{img/fern5000.png}
\caption{Папоротник Барнсли для n = 5000.}
\label{fig:fern5000}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.41\linewidth]{img/fern50000.png}
\caption{Папоротник Барнсли для n = 50000.}
\label{fig:fern50000}
\end{figure}
\newpage
\subsection{Дилемма заключённого}
Дилемма заключённого — фундаментальная проблема в теории игр, согласно которой рациональные игроки не всегда будут сотрудничать друг с другом, даже если это в их интересах. Предполагается, что игрок (<<заключённый>>) максимизирует свой собственный выигрыш, не заботясь о выгоде других. В классическом варианте дилеммы заключённого два игрока могут выбрать одно из двух действий:
@@ -230,24 +231,21 @@
\begin{table}[h!]
\centering
\caption{Исходы дилеммы заключённого}
\caption{Исходы дилеммы заключённого: \( \text{С} \) — сотрудничество, \( \text{П} \) — предательство}
\label{tbl:exodus}
\footnotesize
\begin{tabularx}{\textwidth}{|p{3.5cm}|p{3.5cm}|X|}
\begin{tabular}{|c|c|c|}
\hline
\textbf{Действие игрока 1} & \textbf{Действие игрока 2} & \textbf{Наказание игрока 1 / Наказание игрока 2} \\
& \textbf{Игрок 2: С} & \textbf{Игрок 2: П} \\
\hline
Сотрудничество & Сотрудничество & 1 год / 1 год \\
\textbf{Игрок 1: С} & 1 год / 1 год & 10 лет / 0 лет \\
\hline
Сотрудничество & Предательство & 10 лет / 0 лет \\
\textbf{Игрок 1: П} & 0 лет / 10 лет & 5 лет / 5 лет \\
\hline
Предательство & Сотрудничество & 0 лет / 10 лет \\
\hline
Предательство & Предательство & 5 лет / 5 лет \\
\hline
\end{tabularx}
\end{tabular}
\end{table}
В повторяющейся дилемме заключённого игра происходит периодически, и каждый игрок может «наказать» другого за несотрудничество ранее.
\subsection{Равновесие Нэша}
@@ -311,10 +309,10 @@
\subsubsection{Генерация новой точки}
Генерация новой точки происходит с помощью функции \texttt{genNextPoint} и вспомогательной функции \texttt{applyTransformation}, код которых представлен в листинге~\ref{lst:genDot}. \texttt{applyTransformation} принимает на вход исходную точку и случайное число от 0 до 1, затем выбирает и применяет к точке трансформацию в соответствии с заданными вероятностями, и возвращает новую точку. \texttt{genNextPoint} принимает на вход исходную точку, генерирует случайное число от 0 до 1, применяет функцию \texttt{applyTransformation} и возвращает новую точку.
Генерация новой точки происходит с помощью функции \texttt{genNextPoint} и вспомогательной функции \texttt{applyTransformation}, код которых представлен в листинге~\ref{lst:genDot}. \texttt{applyTransformation} принимает на вход исходную точку и случайное число от 0 до 1, затем выбирает и применяет к точке трансформацию в соответствии с заданными вероятностями, и возвращает новую точку. \texttt{genNextPoint} принимает на вход исходную точку и состояние генератора случайных чисел -- \texttt{gen}, генерирует случайное число от 0 до 1, применяет функцию \texttt{applyTransformation} и возвращает пару -- новую точку и новое состояние генератора случайных чисел.
\begin{lstlisting}[caption={Код функций для генераций новых точек в папоротнике Барнсли.}, label={lst:genDot}]
import System.Random (randomRIO)
import System.Random (StdGen, mkStdGen, randomR)
applyTransformation :: Point -> Float -> Point
applyTransformation point random
@@ -322,40 +320,33 @@
| random < 0.86 = transformation2 point
| random < 0.93 = transformation3 point
| otherwise = transformation4 point
genNextPoint :: Point -> IO Point
genNextPoint point = do
random <- randomRIO (0.0, 1.0 :: Float)
return $ applyTransformation point random
\end{lstlisting}
genNextPoint :: StdGen -> Point -> (Point, StdGen)
genNextPoint gen point =
let (random, newGen) = randomR (0.0, 1.0 :: Float) gen
in (applyTransformation point random, newGen)
\end{lstlisting}
\subsubsection{Рекурсивная генерация папоротника Барнсли}
Функция \texttt{barnsleyFern}, код которой представлен в листинге~\ref{lst:barnsleyFern}, реализует рекурсивный алгоритм генерации списка точек, из которых состоит папоротник Барнсли. Функция принимает на вход начальную точку и число -- количество шагов рекурсии, а возвращает список точек папоротника Барнсли.
Функция \texttt{barnsleyFern}, код которой представлен в листинге~\ref{lst:barnsleyFern}, реализует рекурсивный алгоритм генерации списка точек, из которых состоит папоротник Барнсли. Функция принимает на вход текущее состояние генератора случайных чисел, начальную точку и число -- количество шагов рекурсии, а возвращает список точек папоротника Барнсли.
\begin{lstlisting}[caption={Код функции для построения папоротника Барнсли.}, label={lst:barnsleyFern}]
barnsleyFern :: Point -> Int -> IO [Point]
barnsleyFern _ 0 = return []
barnsleyFern startPoint n = do
x' <- genNextPoint startPoint
xs <- barnsleyFern x' (n - 1)
return (startPoint : xs)
\end{lstlisting}
barnsleyFern :: StdGen -> Point -> Int -> [Point]
barnsleyFern _ _ 0 = []
barnsleyFern gen startPoint n =
let (nextPoint, newGen) = genNextPoint gen startPoint
in startPoint : barnsleyFern newGen nextPoint (n - 1)
\end{lstlisting}
\subsection{Повторяющаяся дилемма заключённого}
\subsubsection{Равновесие Нэша}
Функция \texttt{nashEquilibriumStrategy}, код которой представлен в листинге~\ref{lst:nashEquilibriumStrategy}, реализует равновесие Нэша. Функция рекурсивная, она принимает на вход список действий оппонента, список уже сгенерированных действий и число -- счётчик количества ходов. Функция завершается, когда достигается максимум ходов, либо когда заканчиваются ходы оппонента. Также она использует вспомогательную функцию \texttt{indexOf}. Она принимает на вход некоторый список и элемент этого списка, а возвращает индекс первого совпавшего с указанным элемента. Она возвращает список ходов игрока в соответствии со стратегией.
Функция \texttt{nashEquilibriumStrategy}, код которой представлен в листинге~\ref{lst:nashEquilibriumStrategy}, реализует равновесие Нэша. Функция рекурсивная, она принимает на вход список действий оппонента, список уже сгенерированных действий и число -- счётчик количества ходов. Функция завершается, когда достигается максимум ходов, либо когда заканчиваются ходы оппонента. Она возвращает список ходов игрока в соответствии со стратегией.
\begin{lstlisting}[caption={Код функций, резализующих стратегию в соответствии с равновесием Нэша.}, label={lst:nashEquilibriumStrategy}]
indexOf :: (Eq t) => [t] -> t -> Int
indexOf [] _ = -1
indexOf (x : xs) target
| x == target = 0
| otherwise = 1 + indexOf xs target
\begin{lstlisting}[caption={Код функции, резализующей стратегию в соответствии с равновесием Нэша.}, label={lst:nashEquilibriumStrategy}]
nashEquilibriumStrategy :: [Char] -> [Char] -> Int -> [Char]
nashEquilibriumStrategy opponentMoves generatedMoves n =
if n <= 100 && length opponentMoves > 0
@@ -363,13 +354,12 @@
nashEquilibriumStrategy (tail opponentMoves) (generatedMoves ++ [nextStep]) (n + 1)
else generatedMoves
where
cases = [[' П', ' С'], [' П', ' П']]
results = [[0, 10], [5, 5]]
result =
[ min (results !! 0 !! 1) (results !! 1 !! 1),
max (results !! 0 !! 0) (results !! 1 !! 0)
]
nextStep = cases !! indexOf results result !! 1
cases = [[' С', ' С'], [' С', ' П'], [' П', ' С'], [' П', ' П']]
results = [[1, 1], [10, 0], [0, 10], [5, 5]]
p_years = min (results !! 1 !! 1) (results !! 3 !! 1)
s_years = min (results !! 0 !! 1) (results !! 2 !! 1)
nextStep | p_years <= s_years = ' П'
| otherwise = ' С'
\end{lstlisting}
\subsubsection{Прощающая стратегия}
@@ -379,13 +369,13 @@
\begin{lstlisting}[caption={Код функции, реализующей прощающую стратегию.}, label={lst:forgivingStrategy}]
forgivingStrategy :: [Char] -> [Char] -> Int -> [Char]
forgivingStrategy opponentMoves generatedMoves n
| n > 100 || length opponentMoves == 1 = generatedMoves
| n == 0
= forgivingStrategy opponentMoves (generatedMoves ++ ['С']) (n + 1)
| head opponentMoves == 'С'
= forgivingStrategy (tail opponentMoves) (generatedMoves ++ ['С']) (n + 1)
| otherwise
= forgivingStrategy (tail opponentMoves) (generatedMoves ++ ['П']) (n + 1)
| n > 100 || length opponentMoves == 1 = generatedMoves
| n == 0
= forgivingStrategy opponentMoves (generatedMoves ++ ['С']) (n + 1)
| head opponentMoves == 'С'
= forgivingStrategy (tail opponentMoves) (generatedMoves ++ ['С']) (n + 1)
| otherwise
= forgivingStrategy (tail opponentMoves) (generatedMoves ++ ['П']) (n + 1)
\end{lstlisting}
\subsubsection{Подсчёт очков}
@@ -427,22 +417,21 @@
\label{fig:output1}
\end{figure}
На Рис.~\ref{fig:output2} представлена повторяющаяся дилемма заключённого для прощающей стретегии при заданных ходах игрока: \texttt{['С', 'П', 'С', 'С', 'П', 'П', 'С', 'С', 'П', 'П']}. А на Рис.~\ref{fig:output3} для равновесия по Нэшу.
\begin{figure}[h]
\centering
\includegraphics[width=0.7\linewidth]{img/output2.png}
\caption{Повторяющаяся дилемма заключённого при прощающей стратегии.}
\label{fig:output2}
\centering
\includegraphics[width=0.7\linewidth]{img/output2.png}
\caption{Повторяющаяся дилемма заключённого при прощающей стратегии.}
\label{fig:output2}
\end{figure}
\begin{figure}[h]
\centering
\includegraphics[width=0.7\linewidth]{img/output3.png}
\caption{Повторяющаяся дилемма заключённого для равновесия по Нэшу.}
\label{fig:output3}
\end{figure}
\end{figure}
На Рис.~\ref{fig:output2} представлена повторяющаяся дилемма заключённого для прощающей стретегии при заданных ходах игрока: \texttt{['С', 'П', 'С', 'С', 'П', 'П', 'С', 'С', 'П', 'П']}. А на Рис.~\ref{fig:output3} для равновесия по Нэшу.
\newpage
\section*{Заключение}