aboutsummaryrefslogtreecommitdiff
path: root/Semestr 3/anm/sprawozdanie.tex
diff options
context:
space:
mode:
Diffstat (limited to 'Semestr 3/anm/sprawozdanie.tex')
-rw-r--r--Semestr 3/anm/sprawozdanie.tex488
1 files changed, 0 insertions, 488 deletions
diff --git a/Semestr 3/anm/sprawozdanie.tex b/Semestr 3/anm/sprawozdanie.tex
deleted file mode 100644
index be4f495..0000000
--- a/Semestr 3/anm/sprawozdanie.tex
+++ /dev/null
@@ -1,488 +0,0 @@
-\documentclass{mwart}
-\usepackage{polski}
-
-\setlength{\emergencystretch}{2em}
-\usepackage{datetime}
-\usepackage{ae,aecompl}
-\usepackage[activate={true,nocompatibility},final,tracking=true,kerning=true,spacing=true,stretch=10,shrink=10]{microtype}
-\frenchspacing
-
-%%% fix for \lll
-% \let\babellll\lll
-% \let\lll\relax
-\usepackage{geometry}
-\newgeometry{vmargin={25mm}, hmargin={25mm,25mm}}
-\usepackage[]{algorithm2e}
-
-
-\usepackage{enumitem}
-\usepackage{graphicx}
-\usepackage[normalem]{ulem}
-\usepackage{tikz}
-
-\usetikzlibrary{external}
-\tikzexternalize[prefix=tikz/]
-
-\usetikzlibrary{arrows.meta}
-\usetikzlibrary{matrix, arrows}
-\usepackage{program}
-\usepackage{amsfonts}
-\usepackage{amssymb}
-%%% fix for \lll
-\let\mathlll\lll
-\let\lll\babellll
-
-\usepackage{amsmath}
-\usepackage{amsthm}
-\usepackage{tikz-cd}
-\usepackage{float}
-\usepackage{hyperref}
-\usepackage{multicol}
-\usepackage{mathtools}
-
-\usepackage{array}
-\usepackage{wrapfig}
-\usepackage{multirow}
-\usepackage{tabularx}
-\newcommand{\RR}{\mathbb{R}}
-\newcommand{\CC}{\mathbb{C}}
-\DeclarePairedDelimiter\abs{\lvert}{\rvert}%
-
-\newcommand{\fC}{{\mathfrak C}}
-\newcommand{\cM}{{\mathcal M}}
-\newcommand{\cC}{{\mathcal C}}
-\newcommand{\cD}{{\mathcal D}}
-\newcommand{\bN}{{\mathbf{N}}}
-\newcommand{\bR}{{\mathbf{R}}}
-\newcommand{\bZ}{{\mathbf{Z}}}
-\newcommand{\bF}{{\mathbf{F}}}
-\newcommand{\bQ}{{\mathbf{Q}}}
-\newcommand{\bC}{{\mathbf{C}}}
-\newcommand{\cA}{{\mathcal A}}
-\newcommand{\cO}{{\mathcal O}}
-\newcommand{\cF}{{\mathcal F}}
-\newcommand{\cB}{{\mathcal B}}
-\newcommand{\Ob}{{\mathrm{Ob}}}
-\newcommand{\topl}{\mathcal T}
-\newcommand{\Set}{{\mathrm{Set}}}
-\newcommand{\Grp}{{\mathrm{Grp}}}
-\newcommand{\AbGrp}{{\mathrm{AbGrp}}}
-\newcommand{\Mod}{{\mathrm{Mod}}}
-\newcommand{\Ring}{{\mathrm{Ring}}}
-\newcommand{\Vect}{{\mathrm{Vect}}}
-\newcommand{\Alg}{{\mathrm{Alg}}}
-\newcommand{\restr}{\mathord{\upharpoonright}}
-\newcommand{\liff}{\mathrel{\leftrightarrow}}
-\newcommand{\limplies}{\mathrel{\rightarrow}}
-\newcommand{\fset}[1]{\left\{{#1}\right\}}
-\newcommand{\meet}{\mathbin{\wedge}}
-\newcommand{\biglor}{\bigvee}
-\newcommand{\bigland}{\bigwedge}
-
-
-\DeclareMathOperator{\round}{{round}}
-\DeclareMathOperator{\cl}{{cl}}
-\DeclareMathOperator{\Id}{{Id}}
-\DeclareMathOperator{\id}{{id}}
-\DeclareMathOperator{\Aut}{{Aut}}
-\DeclareMathOperator{\End}{{End}}
-\DeclareMathOperator{\Ult}{{Ult}}
-\DeclareMathOperator{\Homeo}{{Homeo}}
-\DeclareMathOperator{\dom}{{dom}}
-\DeclareMathOperator{\rng}{{rng}}
-\DeclareMathOperator{\Core}{{Core}}
-\DeclareMathOperator{\Hom}{{Hom}}
-\DeclareMathOperator{\Stab}{{Stab}}
-\DeclareMathOperator{\dcl}{{dcl}}
-\DeclareMathOperator{\acl}{{acl}}
-\DeclareMathOperator{\tp}{{tp}}
-\DeclareMathOperator{\characteristic}{{char}}
-
-
-
-\newtheorem{twr}{Twierdzenie}[section]
-\newtheorem{hip}[twr]{Hipoteza}
-\newtheorem{pyt}[twr]{Pytanie}
-\newtheorem{problem}[twr]{Problem}
-\newtheorem{lem}[twr]{Lemat}
-\newtheorem{fkt}[twr]{Fakt}
-\newtheorem{wnsk}[twr]{Wniosek}
-\newtheorem{stw}[twr]{Stwierdzenie}
-\newtheorem{cw}[twr]{Ćwiczenie}
-
-\theoremstyle{remark}
-\newtheorem{uwg}[twr]{Uwaga}
-\theoremstyle{definition}
-\newtheorem{dfn}[twr]{Definicja}
-\newtheorem*{rozw}{Rozwiązanie}
-\newtheorem*{sbclm}{Podclaim}
-\newtheorem*{clm*}{Claim}
-\newtheorem{pd}[twr]{Przykład}
-\newcounter{claimcounter}[twr]
-\newenvironment{clm}{\stepcounter{claimcounter}{\noindent {\textbf{Claim}} \theclaimcounter:}}{}
-\newenvironment{clmproof}[1][\proofname]{\proof[#1]\renewcommand{\qedsymbol}{$\square$(claim)}}{\endproof}
-\newenvironment{sbclmproof}[1][\proofname]{\proof[#1]\renewcommand{\qedsymbol}{$\square$(subclaim)}}{\endproof}
-
-\newcommand{\xqed}[1]{%
- \leavevmode\unskip\penalty9999 \hbox{}\nobreak\hfill
- \quad\hbox{\ensuremath{#1}}}
-\theoremstyle{definition}
-\newtheorem{zad}[twr]{Zadanie}
-
-\title{Pracownia z analizy numerycznej \\
- \large Sprawozdanie do zadania \textbf{P1.10} \\
- Prowadzący: dr Rafał Nowak}
-\author{Franciszek Malinka, Kacper Solecki}
-\date{Wrocław, Listopad 2020}
-
-\begin{document}
-
-\maketitle
-
-\section{Wstęp}
-Funkcje trygonometryczne mają szerokie zastosowania w matematyce, informatyce, inżynierii, architekturze, produkcji muzyki i wielu innych dziedzinach. Nietrudno zatem dojść do wniosku, że ich efektywne i dokładne obliczanie jest problemem bardzo ważnym w kontekście tych zagadnień.
-
-W niniejszym sprawozdaniu przyjrzymy się dwóm opracowanym przez nas metodom obliczania wybranych funkcji trygonometrycznych używając jednie najprostszych operacji arytmetycznych ($+$, $-$, $*$, $/$, ale też przesunięcia bitowe), ze szczególnym naciskiem na dokładne obliczanie funkcji $\sin$ oraz $\cos$, również w dziedzinie liczb zespolonych.
-
-Proponowane przez nas metody mają docelowo dawać poprawne obliczenia dla podwójnej precyzji obliczeń, jednakże testy numeryczne przeprowadzamy używając zmiennych typu \texttt{BigFloat} w języku \texttt{Julia} (w którym implementowaliśmy nasze rozwiązania). Typ ten oferuje dowolną dokładność obliczeń. Wyniki naszych funkcji porównujemy z funkcjami bibliotecznymi języka i zakładamy, że dają one dokładne wyniki.
-
-\section{Algorytm CORDIC}
-\subsection{Opis algorytmu}
-
-Pierwszą proponowaną przez nas metodą obliczania funkcji $\sin$ oraz $\cos$ jest Algorytm CORDIC (\textbf{CO}ordinate \textbf{R}otation \textbf{DI}gital \textbf{C}omputer). Algorytm ten został stworzony z myślą o komputerach o niskiej mocy obliczeniowej, ale również o możliwości ''włożenia'' algorytmu w hardware (tj. pozwala tworzyć mało skomplikowane układy bramek logicznych, które obliczają funkcje trygonometryczne). Jak się przekonamy, proces iteracyjny algorytmu korzysta jedynie z dodawania, odejmowania, przesunięć bitowych i wartości obliczonych podczas preprocessingu oraz nie wykorzystuje liczb zmiennoprzecinkowych.
-
-Zacznijmy od wprowadzenia zarysu działania algorytmu. Zapomnijmy na razie o analizie numerycznej i przenieśmy się do świata algebry liniowej. Wyobraźmy sobie, że mamy wydajny system który obliczy wektor $(x_r, y_r)$ jako wynik obrotu danego wektora $(x_0, y_0)$ o dany kąt $\theta$ wokół środka układu współrzędnych:
-\begin{align}
- x_r = x_0\cos\theta - y_0\sin\theta, \\
- y_r = x_0\sin\theta + y_0\cos\theta.
-\end{align}
-
-Jeśli za $(x_0, y_0)$ weźmiemy punkt $(1, 0)$, to po obrocie dostaniemy:
-\begin{align*}
- x_r = \cos\theta, \\
- y_r = \sin\theta.
-\end{align*}
-Zatem używając obrotu umiemy policzyć wartości funkcji $\cos$ oraz $\sin$.
-
-Zapiszmy równania $(1), (2)$ w formie macierzowej:
-\begin{align}
- \begin{bmatrix}
- x_r \\ y_r
- \end{bmatrix}
- = \begin{bmatrix}
- \cos\theta & -\sin\theta \\
- \sin\theta & \cos\theta
- \end{bmatrix}
- \begin{bmatrix}
- x_0 \\ y_0
- \end{bmatrix}
- = \cos\theta
- \begin{bmatrix}
- 1 & -\tan\theta \\
- \tan\theta & 1
- \end{bmatrix}
- \begin{bmatrix}
- x_0 \\ y_0
- \end{bmatrix}.
-\end{align}
-
-Powyższa równość pokazuje, że do obliczenia naszego wektora wynikowego (przy założeniu, że znamy wartości $\tan\theta$ oraz $\cos\theta$) wystarczą jedynie 4 mnożenia i kilka dodawań lub odejmowań. Chcielibyśmy pozbyć się tych mnożeń. Skorzystamy tutaj z dwóch obserwacji:
-\begin{itemize}
- \item Każdy kąt $\theta\in [0^{\circ}, 90^{\circ}]$ możemy zapisać jako sumę \textbf{wcześniej ustalonych}, mniejszych (co do modułu) kątów $\theta_i, i \in \{0, ..., n\}$:
- \begin{align}
- \theta = \sum_{i=0}^n \sigma_i\theta_i, \; \sigma_i \in \{-1, 1\}.
- \end{align}
- Dla przykładu, kąt $57.353^{\circ}$ jest sumą kątów
- $45^{\circ}, 26.565^{\circ}, -14.03^{\circ}$ (dobór tych kątów jest nieprzypadkowy, o czym się zaraz przekonamy).
- Jeśli $\theta$ nie należy do zadanego przez nas przedziału, to możemy ten kąt zmienić korzystając ze wzorów redukcyjnych
- (o tym więcej w \textsection 3).
- \item Jeśli nasze kąty $\theta_i$ będą dobrane tak, że $\tan\theta_i = 2^{-i}$, to mnożenie przez $\tan\theta_i$ jest niczym innym jak przesunięciem bitowym (w liczbach całkowitych). Dodatkowo okazuje się, że dowolny kąt nie większy niż $90^{\circ}$ da się przybliżyć sumą tak dobranych kątów $\theta_i$, więc da się tymi kątami osiągnąć cel założony w pierwszym punkcie. Dodatkowo im więcej takich kątów wybierzemy, tym dokładniejsze będzie to przybliżenie.
-\end{itemize}
-
-Pozostały nam jeszcze mnożenia przez czynnik $\cos\theta$ (który nazwiemy przyrostem). Jeżeli to zignorujemy, to otrzymana rotacja będzie faktycznie obróceniem wektora o kąt $\theta$, ale z dodatkowym przeskalowaniem wektora.
-
-% \begin{center}
-% \begin{tikzpicture}
-% \draw [<->,thick] (0,6) node (yaxis) [above] {$y$}
-% |- (8,0) node (xaxis) [right] {$x$};
-% \draw [->, thick] (0, 0) -- (6, 2) node (v1) [right] {$(x_0, y_0)$}
-% \draw [->, cm={cos(45) ,-sin(45) ,sin(45) ,cos(45) ,(0 cm, 0 cm)}] (0, 0) -- (6,2)
-% % \draw[black, thick, ->] (0,0) -- (10,0);
-% % \draw[black, thick, ->] (0,0) -- (0,8);
-% \end{tikzpicture}
-% \end{center}
-
-Przyjrzyjmy się jak dokładnie będzie wyglądać nasz przyrost, jeśli zastosujemy zaproponowane przez nas punkty do obliczania obrotu. Powiedzmy, że chcemy obrócić wejściowy wektor o kąt $57.353^{\circ} = 45^{\circ} + 26.565^{\circ} - 14.03^{\circ}$. Wartości funkcji $\tan$ tych kątów są odwrotnościami potęg dwójki, zatem te kąty spełniają nasze założenie. Pierwsza rotacja o $45^{\circ}$ daje:
-\begin{align}
- \begin{bmatrix}
- x_1 \\ y_1
- \end{bmatrix}
- = \cos 45^{\circ}
- \begin{bmatrix}
- 1 & -1 \\
- 1 & 1
- \end{bmatrix}
- \begin{bmatrix}
- x_0 \\ y_0
- \end{bmatrix}.
-\end{align}
-Druga rotacja daje:
-\begin{align}
- \begin{bmatrix}
- x_2 \\ y_2
- \end{bmatrix}
- = \cos 26.565^{\circ}
- \begin{bmatrix}
- 1 & -2^{-1} \\
- 2^{-1} & 1
- \end{bmatrix}
- \begin{bmatrix}
- x_1 \\ y_1
- \end{bmatrix}.
-\end{align}
-Trzecia rotacja:
-\begin{align}
- \begin{bmatrix}
- x_3 \\ y_3
- \end{bmatrix}
- = \cos(-14.03^{\circ})
- \begin{bmatrix}
- 1 & 2^{-2} \\
- -2^{-2} & 1
- \end{bmatrix}
- \begin{bmatrix}
- x_2 \\ y_2
- \end{bmatrix}.
-\end{align}
-Łącząc te równania razem dostajemy:
-\begin{align}
- \begin{bmatrix}
- x_3 \\ y_3
- \end{bmatrix}
- = \cos 45^{\circ}\cos 26.565^{\circ}\cos(-14.03^{\circ})
- \begin{bmatrix}
- 1 & -1 \\
- 1 & 1
- \end{bmatrix}
- \begin{bmatrix}
- 1 & -2^{-1} \\
- 2^{-1} & 1
- \end{bmatrix}
- \begin{bmatrix}
- 1 & 2^{-2} \\
- -2^{-2} & 1
- \end{bmatrix}
- \begin{bmatrix}
- x_0 \\ y_0
- \end{bmatrix}.
-\end{align}
-
-Zauważmy, że dzięki parzystości funkcji $\cos$ znak poszczególnych kątów nie ma znaczenia dla wartości przyrostu. Z tego snujemy wniosek, że przy ustalonej liczbie iteracji przyrost nie zależy od wyboru kąta $\theta$. Możemy go zatem policzyć i wziąć go pod uwagę dopiero na koniec obliczeń.
-\begin{align}
- P = \cos 45^{\circ}\cdot\cos 26.565^{\circ}\cdot\cos 14.03^{\circ}\cdot\ldots \approx 0.6072.
-\end{align}
-W takim razie, pomijając przyrost $P$ otrzymujemy następujący proces iteracyjny algorytmu CORDIC:
-\begin{align}
- x_{i + 1} & = x_{i} - \sigma_i 2^{-i}y_i, \\
- y_{i + 1} & = y_i + \sigma_i 2^{-i}y_i.
-\end{align}
-
-Pozostaje jedynie problem znajdowania znaków $\sigma_i$ przy kątach $\theta_i$. Okazuje się jednak, że możemy to robić w bardzo prosty sposób. Niech $z_0 = \theta, \sigma_0 = 1$. W każdym kroku iteracyjnym znak $\sigma_{i + 1}$ dobieramy w następujący sposób -- niech $z_{i}$ będzie równe $\theta - \sum_{k=0}^{i - 1}\sigma_k\theta_k$ (czyli $z_i$ mówi jaki jeszcze nam został kąt do obrócenia, potencjalnie obróciliśmy już za dużo, wtedy $z_i < 0$). Wtedy $\sigma_{i + 1} = sgn(z_i)$. Mamy też $z_{i + 1} = z_{i} - \sigma_i\theta_i = z_{i} - \sigma_i\arctan{2^{i}}$. Błąd przybliżenia po $n$ iteracjach możemy wtedy łatwo policzyć ze wzoru
-\begin{align}
- \theta_{error} = z_n = \theta - \sum_{i=0}^n\sigma_i \theta_i.
-\end{align}
-
-Zbierając wszystko razem, proces iteracyjny algorytmu CORDIC wygląda następująco:
-\begin{align*}
- x_{i + 1} & = x_{i} - \sigma_i 2^{-i}y_i, \\
- y_{i + 1} & = y_i + \sigma_i 2^{-i}y_i, \\
- z_{i + 1} & = z_i - \sigma_i \arctan 2^{-1}.
-\end{align*}
-
-Kąty $\theta_i = \arctan{2^{-1}}$ możemy policzyć w preprocessingu i używać jako stałych. Wtedy rezultatem naszych obliczeń będzie $\cos\theta \approx P\cdot x_n$ oraz $\sin\theta \approx P\cdot y_n$. Dodatkowo, gdybyśmy przyjęli $x_0 = 1/P$, to pozbylibyśmy się nawet tego ostatniego mnożenia.
-
-Warto jeszcze zauważyć, że
-\begin{align*}
- \frac{1}{\cos(\arctan 2^{-i})} = \sqrt{1 + \frac{1}{2^{2i}}}.
-\end{align*}
-Możemy ten fakt wykorzystać do dokładniejszego obliczania wartości $P$.
-
-Musimy jeszcze zauważyć, że algorytm działa jedynie dla kątów $\theta$ spełniających
-\begin{align*}
- \abs{\theta} \leq \sum_{i=0}^n\theta_i \approx 99.88^{\circ}.
-\end{align*}
-Zatem dla kątów większych niż $90^{\circ}$ musimy skorzystać ze wzorów redukcyjnych, co dokłada pewnego błędu do naszego wyniku oraz powoduje konieczność wykonania kilku dodatkowych dzieleń i mnożeń.
-
-\subsection{Niespełniona obietnica}
-We wstępie powiedzieliśmy, że algorytm będzie korzystał z dodawań, odejmowań i przesunięć bitowych, a do tego używał liczb całkowitych. Dzięki naszemu ustaleniu, że $\arctan\theta_i = 2^{-i}$, wszystkie mnożenia podczas iteracji naszego algorytmu to mnożenia przez potęgi dwójki. Jak możemy to wykorzystać?
-
-Ustalmy $M := 2^{K}$ dla pewnego $K$ (potem je wybierzemy). Teraz każdą spreprocessowaną
-przez nas wartość $T$ (czyli $T$ jest kątem $\theta_i$ lub przyrostem $P$) przyjmiemy
-$T := \round(M \cdot T)$. Chcąc policzyć wartości funkcji trygonometrycznych dla kąta $\theta$,
-uruchomimy nas proces iteracyjny dla $x_0 = \round(M/P)$, $y_0 = 0$, $z_0 = \round(M\cdot\theta)$.
-Zauważmy, że dzięki temu przeskalowaliśmy wszystkie obliczane przez nas wartości o stałą $M$
-i zaokrągliliśmy je po to, by móc pracować na liczbach całkowitych. To pozawala na wykorzystanie
-przesunięć bitowych podczas mnożenia przez potęgi dwójki, dzięki czemu znacznie zwiększyliśmy
-wydajność naszego algorytmu. Wtedy, po $n$ iteracjach naszego procesu mamy $\cos\theta \approx x_n/M$
-oraz $\sin\theta\approx y_n/M$ (już w arytmetyce zmiennoprzecinkowej).
-
-Zostało nam ustalić wartość $K$. Na pewno chcielibyśmy, aby $K$ było nie większe niż
-długość mantysy. Ponadto algorytm ma być dostosowany do mało wydajnych maszyn, dlatego
-w naszych analizach pracujemy przy użyciu \texttt{Int32}, zatem nie chcemy żeby $2^K\cdot T$ przekroczyło
-zakres \texttt{Int32}. Jednakże kąty $\theta_i$ oraz wartość $P$ są niewielkie, zatem $K = 30$ będzie
-odpowiednią wartością.
-
-\section{Wzór Taylora}
-\subsection{Opis metody}
-Zanim przejdziemy do opisu tej metody, przypomnijmy sobie pewną tożsamość trygonometryczną:
-\begin{align}
- \sin z = \sin (x + yi) = \sin x\cosh (y) + i\cos x\sinh(y).
-\end{align}
-Korzystając z tej tożsamości pozbywamy się konieczności pracowania z liczbami zespolonymi i możemy operować jedynie w zbiorze liczb rzeczywistych.
-
-W tej metodzie wykorzystamy znany analityczny wzór zwany wzorem Taylora. Korzystając z niego możemy wyprowadzić rozwinięcia funkcji trygonometrycznych:
-
-\begin{align*}
- \sin x & = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \ldots, \\
- \sinh x & = x + \frac{x^3}{3!} + \frac{x^5}{5!} + \frac{x^7}{7!} + \ldots, \\
- \cos x & = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \ldots, \\
- \cosh x & = 1 + \frac{x^2}{2!} + \frac{x^4}{4!} + \frac{x^6}{6!} + \ldots. \\
-\end{align*}
-
-Obliczanie rozwinięć poszczególnych funkcji jest proste i wyabstrahowaliśmy je do jednej, generycznej funkcji \texttt{TalyorSeries}:
-\begin{center}
- \begin{algorithm}[H]
- \SetAlgoLined
- \KwData{x, parity, changeSign, M}
- \KwResult{Obliczenie szeregu Taylora odpowiedniej funkcji trygonometrycznej w punkcie x dla jego pierwszych M niezerowych wyrazów}
- result := 0\;
- elem := 1\;
- \If{parity = 1}{
- elem := x\;
- }
- i := parity + 1\;
- \While{i $\le$ 2M + parity}{
- result := result + elem\;
- elem := elem * changeSign * x * x / (i * (i + 1))\;
- i := i + 2\;
- }
- \end{algorithm}
-\end{center}
-
-Algorytm oblicza sumę $\sum_{n=0}^M\sigma_n\frac{x^n}{n!}$, gdzie $\sigma_i \in \{-1, 0, 1\}$. Wartość $\sigma_n$ zależy od wartości parametrów podanych w funkcji: gdzy \texttt{parity} jest równe $0$, wtedy mamy $\sigma_{2k + 1} = 0$, a gdy \texttt{parity} jest równe $0$ mamy $\sigma_{2k} = 0$. Odpowiada to odpowiednio szeregom $\cos x, \cosh x$ oraz $\sin x, \sinh x$. Od parametru \texttt{changeSign} zależy czy chcemy, aby kolejne niezerowe wyrazy obliczanego szeregu zmieniały znak (zmieniamy znak, gdy chcemy obliczać zwykłe funkcje trygonometryczne oraz nie zmieniamy gdy obliczamy funkcje hiperboliczne).
-
-To daje prostą możliwość obliczania pożądanych przez nas funkcji:
-
-\begin{align*}
- \sin x & = \texttt{TaylorSeries}(x, 1, -1, M), \\
- \sinh x & = \texttt{TaylorSeries}(x, 1, 1, M), \\
- \cos x & = \texttt{TaylorSeries}(x, 0, -1, M), \\
- \cosh x & = \texttt{TaylorSeries}(x, 0, 1, M).
-\end{align*}
-
-Zauważmy, że wzór Taylora nadaje się do przybliżania funkcji trygonometrycznych jedynie dla argumentów bliskich $0$.
-Na szczęście możemy sobie z tym poradzić korzystając ze znanych tożsamości trygonometrycznych oraz okresowości funkcji $\sin$ i $\cos$.
-Naszym celem przed obliczniem funkcji \texttt{TaylorSeries} będzie sprowadzenie argumentu do przedziału $[0, \pi/4]$,
-w którym wzór Taylora bardzo dobrze przybliża wartości funkcji trygonometrycznych.
-Oto tabela która przedstawia jak radzimy sobie z argumentami spoza tego przedziału:
-\begin{table}[H]
- \centering
- \begin{tabular}{ |p{4cm}||p{4cm}|p{4cm}| }
- \hline
- \multicolumn{3}{|c|}{Wzory redukcyjne} \\
- \hline
- Warunek na $x$ & $\sin x$ & $\cos x$ \\
- \hline
- $x < 0$ & $-\sin (-x)$ & $\cos (-x)$ \\
- $x \ge 2\pi$ & $\sin(x \mod 2\pi)$ & $\cos (x\mod 2\pi)$ \\
- $x > \pi$ & $-\sin(x - \pi)$ & $-\cos(x - \pi)$ \\
- $x > \pi/2$ & $\cos(x - \pi/2)$ & $-\sin(x - \pi/2)$ \\
- $x > \pi/4$ & $\cos(\pi/2 - x)$ & $\sin(\pi/2 - x)$ \\
- \hline
- \end{tabular}
- \caption{Wzory redukcyjne.}
- \label{tab:reduk}
-\end{table}
-
-Dla funkcji hiperbolicznych sposób jest prostszy: korzystamy z dwóch własności:
-\begin{align*}
- \sinh x & = 2\sinh(x/2)\cosh(x/2), \\
- \cosh x & = \cosh^2(x/2) + \sinh^2(x/2).
-\end{align*}
-Można by przypuszczać, że dla dużych $x$ błąd obliczania tych funkcji będzie duży. Jednakże okazuje się, że funkcje te bardzo szybko rosną i już dla $x = 1000$ wartości obu tych funkcji nie mieszczą się w zakresie \texttt{Float64}, zatem tak naprawdę wykonamy maksymalnie $15$ takich redukcji, co generuje dopuszczalnie mały błąd.
-
-\section{Analiza błędu}
-
-\subsection{Wyniki testów}
-Dokładność naszych metod porównywaliśmy z funkcjami bibliotecznymi w języku \texttt{Julia}, które domyślnie obsługują obliczanie wartości funkcji trygonometrycznych dla liczb zespolonych. Zakładmy o tych funkcjach bibliotecznych, że dają poprawny wynik.
-
-Przeprowadziliśmy testy dokładności metody opartej na wzorze Taylora dla liczb rzeczywistych oraz dla liczb zespolonych oraz testy dla metody Taylora, w której nie używaliśmy wzorów redukcyjnych, lecz rozwijaliśmy wzór dopóki wystarczająco dobrze nie przybliżał wartości funkcji dla danego argumentu. Testy dla algorytmu CORDIC przeprowadziliśmy wyłącznie dla liczb rzeczywistych.
-
-Dla każdej metody przeprowadziliśmy trzy rodzaje testów, w każdym z nich losowaliśmy $10^8$ liczb z różnych przedziałów. Ze względu na podobieństwo funkcji $\sin$ i $\cos$ oraz z faktu, że często wzory redukcyjne powodują faktycznie obliczanie innej funkcji trygonometrycznej, testy przeprowadziliśmy wyłącznie dla funkcji $\sin$. Przedziały i wyniki testów przedstawione są w poniższej tabeli:
-
-\begin{table}[H]
- \centering
- \resizebox{\textwidth}{!}{%
- {\setlength{\extrarowheight}{5pt}%
- \begin{tabular}{ |c||c|c|c|c|c| }
- \hline
- algorytm & przedział argumentu & średni błąd wz. & max błąd wz. & średni błąd bezwz. & max błąd bezwz. \\
- \hline
- \multirow{3}{6em}{Taylor dla $\RR$} & $x:$ dowolny Float64 & $1,887 \cdot 10^{-15}$ & $3,167 \cdot 10^{-8}$ & $1,179 \cdot 10^{-16}$ & $8,882 \cdot 10^{-16}$ \\
- \cline{2-6}
- & $-2\pi \leq x \leq 2\pi$ & $1,472 \cdot 10^{-15}$
- & $1.184 \cdot 10^{-8}$ & $9,766 \cdot 10^{-17}$ & $5,551 \cdot 10^{-16}$ \\
- \cline{2-6}
- & $0 \leq x \leq 1$ & $8,694 \cdot 10^{-17}$ & $6,661 \cdot 10^{-16}$ & $4,293 \cdot 10^{-17}$ & $4,441 \cdot 10^{-16}$ \\
- \hline
- \multirow{3}{6em}{Taylor dla $\CC$} & $-100 \leq \abs{x} \leq 100$ & $4,932 \cdot 10^{-15}$ & $1,311 \cdot 10^{-13}$ & $1,689 \cdot 10^{26}$ & $5,898 \cdot 10^{29}$ \\
- \cline{2-6}
- & $-2\pi \leq \abs{x} \leq 2\pi$ & $4,338 \cdot 10^{-16}$ & $1,487 \cdot 10^{-11}$ & $1,364 \cdot 10^{-14}$ & $8,710 \cdot 10^{-13}$ \\
- \cline{2-6}
- & $0 \leq \abs{x} \leq 1$ & $1,597 \cdot 10^{-16}$ & $1,099 \cdot 10^{-15}$ & $1,124 \cdot 10^{-16}$ & $1,111\cdot 10^{-15}$ \\
- \hline
- \multirow{3}{6em}{Taylor dla $\CC$ bez wzorów redukcyjnych} & $-100 \leq \abs{x} \leq 100$ & $4,77 \cdot 10^{23}$ & $4,488 \cdot 10^{26}$ & $7,759 \cdot 10^{40}$ & $2,208 \cdot 10^{44}$ \\
- \cline{2-6}
- & $-2\pi \leq \abs{x} \leq 2\pi$ & $6,333 \cdot 10^{-1}$ & $1,000$ & $2,344 \cdot 10$ & $2,677 \cdot 10^2$ \\
- \cline{2-6}
- & $0 \leq \abs{x} \leq 1$ & $1,589 \cdot 10^{-16}$ & $1,291 \cdot 10^{-15}$ & $1,118 \cdot 10^{-16}$ & $1,116 \cdot 10^{-15}$ \\
- \hline
- \multirow{3}{6em}{Cordic dla $\RR$} & $x:$ dowolny Float64 & $3,100 \cdot 10^{-8}$ & $4,575 \cdot 10^{-1}$ & $2,459 \cdot 10^{-9}$ & $5,529 \cdot 10^{-3}$ \\
- \cline{2-6}
- & $-2\pi \leq x \leq 2\pi$ & $2,770 \cdot 10^{-8}$ & $1,183 \cdot 10^{-1}$ & $2,532 \cdot 10^{-9}$ & $6,042 \cdot 10^{-4}$ \\
- \cline{2-6}
- & $0 \leq x \leq 1$ & $4,176 \cdot 10^{-8}$ & $9,182 \cdot 10^{-2}$ & $2,614 \cdot 10^{-9}$ & $5,261 \cdot 10^{-4}$ \\
- \hline
- \end{tabular}
- }}
- \caption{Błędy przy obliczaniu funkcji $\sin(x)$.}
- \label{tab:2}
-\end{table}
-
-\subsection{Wnioski}
-Jak widać w tabeli \ref{tab:2}, dla wszystkich testów zaproponowane przez nas metody sprawdzają
-się bardzo dobrze dla małych argumentów. Algorytm CORDIC wypada dużo gorzej od metody korzystającej
-ze wzoru Taylora, lecz nie jest to dla nas nic zaskakującego -- metoda ta tworzy kompromis
-między wydajnością, a dokładnością obliczeń. Dla obu metod widać, że problemem jest zmiana
-argumentu na mały, gdyż to generuje największy błąd. W obu przypadkach najgorszy błąd względny
-generowały argumenty, które są duże i zbliżone do wielokrotności $\pi$, co wynika z konieczności odejmowania,
-z którego korzysta wbudowana w \texttt{Julia} funkcja \texttt{mod2pi} oraz wzory redukcyjne.
-Prowadzi do utraty cyfr znaczących, tym samym obniżając dokładność obliczeń.
-
-Dużym problemem w obliczaniu wartości funkcji trygonometrycznych w dziedzinie liczb zespolonych jest konieczność używa funkcji hiperbolicznych, które rosną w tempie wykładniczym. Jeśli spojrzymy na wzór $(13)$ to zauważmy, że bardzo prawdopodobne jest, że będziemy mnożyć zbliżoną do $0$ wartość funkcji $\sin$ oraz $\cos$ z potencjalnie bardzo dużymi wartościami funkcji $\cosh$ i $\sinh$.
-
-Mimo to jesteśmy zadowoleni z rezultatów dla losowych testów -- jak widać, średni błąd względny jest rzędu dokładności liczb o precyzji podwójnej w przypadku metody Taylora oraz rzędu pojedynczej precyzji dla algorytmu CORDIC (co wynika z użycia \texttt{Int32} podczas procesu iteracyjnego).
-
-\begin{thebibliography}{9}
- \bibitem{CORDIC tutorial}
- Steve Arar.
- \textit{An Introduction to the CORDIC Algorithm}.
- \\\texttt{\url{https://www.allaboutcircuits.com/technical-articles/an-introduction-to-the-cordic-algorithm/}}
-
- \bibitem{CORDIC ints}
- Andrea Vitali.
- \textit{Coordinate rotation digital computer algorithm (CORDIC)
- to compute trigonometric and hyperbolic functions}.
- \\\texttt{\url{https://bit.ly/3lVQxbJ}}
-\end{thebibliography}
-\end{document} \ No newline at end of file