MN10: Różnice pomiędzy wersjami
mNie podano opisu zmian |
Nie podano opisu zmian |
||
Linia 1: | Linia 1: | ||
<!-- | <!-- | ||
Konwertowane z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/ pawlik1/latex2mediawiki.php | Konwertowane z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/ pawlik1/latex2mediawiki.php | ||
Linia 9: | Linia 8: | ||
wyznaczania dyskretnej transformacji cosinusów (DCT i MDCT), choć dotyczą | wyznaczania dyskretnej transformacji cosinusów (DCT i MDCT), choć dotyczą | ||
pozornie dość abstrakcyjnych zadań, zrewolucjonizowały wiele dziedzin życia. | pozornie dość abstrakcyjnych zadań, zrewolucjonizowały wiele dziedzin życia. | ||
Między innymi | Między innymi wykorzystuje się je w | ||
* kompresji obrazów w formacie JPEG (DCT) | * kompresji obrazów w formacie JPEG (DCT) | ||
* kompresji dźwięku w formacie MP3 i pokrewnych (MDCT) | * kompresji dźwięku w formacie MP3 i pokrewnych (MDCT) | ||
Linia 21: | Linia 20: | ||
W niniejszym wykładzie ograniczymy się do przedstawienia szybkiego algorytmu | W niniejszym wykładzie ograniczymy się do przedstawienia szybkiego algorytmu | ||
rozwiązywania zadania DFT | rozwiązywania zadania DFT. Algorytmy rozwiązywania zadań pokrewnych (DCT, MDCT, | ||
itp.) opierają się na podobnych zasadach. | itp.) opierają się na podobnych zasadach. | ||
Linia 28: | Linia 27: | ||
Dla danego zestawu <math>\displaystyle N</math> liczb <math>\displaystyle f_\alpha\in C</math>, <math>\displaystyle \alpha = 0,\ldots, N-1</math>, | Dla danego zestawu <math>\displaystyle N</math> liczb <math>\displaystyle f_\alpha\in C</math>, <math>\displaystyle \alpha = 0,\ldots, N-1</math>, | ||
wyznacz <math>\displaystyle N</math> wartości | |||
<center><math>\displaystyle c_j = \sum_{\alpha = 0}^{N-1} f_\alpha \omega_N^{j\alpha}, | <center><math>\displaystyle c_j = \sum_{\alpha = 0}^{N-1} f_\alpha \omega_N^{j\alpha}, | ||
Linia 79: | Linia 78: | ||
ma tylko <math>\displaystyle N</math> różnych wyrazów), gdyż <math>\displaystyle \omega_N^N = 1</math> (dla | ma tylko <math>\displaystyle N</math> różnych wyrazów), gdyż <math>\displaystyle \omega_N^N = 1</math> (dla | ||
<math>\displaystyle j=0,\ldots,N-1</math>, <math>\displaystyle \omega_N^j</math> to nic innego jak wszystkie zespolone pierwiastki z jedynki), a | <math>\displaystyle j=0,\ldots,N-1</math>, <math>\displaystyle \omega_N^j</math> to nic innego jak wszystkie zespolone pierwiastki z jedynki), a | ||
jak wiadomo, mnożenie przez 1 nic nie kosztuje | jak wiadomo, mnożenie przez 1 nic nie kosztuje, o ile tylko się wie, <strong>gdzie</strong> jest ta jedynka. Z grubsza na tym właśnie będzie polegać siła algorytmu | ||
FFT! | FFT! | ||
W wyprowadzeniu algorytmu szybkiej transformacji Fouriera, ''Fast Fourier | W wyprowadzeniu algorytmu szybkiej transformacji Fouriera, ''Fast Fourier | ||
Transform, FFT ''oprzemy się | Transform, FFT ''oprzemy się po raz kolejny na regule "dziel i rządź". Dla | ||
uproszczenia analizy przyjmiemy, że <math>\displaystyle N</math> jest naturalną potęgą dwójki, w | uproszczenia analizy przyjmiemy, że <math>\displaystyle N</math> jest naturalną potęgą dwójki, w | ||
szczególności <math>\displaystyle N = 2m</math> dla pewnego naturalnego <math>\displaystyle m</math>. | szczególności <math>\displaystyle N = 2m</math> dla pewnego naturalnego <math>\displaystyle m</math>. | ||
Linia 123: | Linia 122: | ||
\endaligned</math></center> | \endaligned</math></center> | ||
(jak widać są to DFT dla dwa razy krótszych wektorów | (jak widać są to DFT dla dwa razy krótszych wektorów złożonych z tylko | ||
parzystych lub tylko nieparzystych współrzędnych <math>\displaystyle f</math>) dostajemy ostatecznie | parzystych lub tylko nieparzystych współrzędnych <math>\displaystyle f</math>), dostajemy ostatecznie | ||
<center><math>\displaystyle \aligned c_{\widetilde{j}} = \Phi_{\widetilde{j}} + \omega_N^{\widetilde{j}} \Psi_{\widetilde{j}},\\ | <center><math>\displaystyle \aligned c_{\widetilde{j}} = \Phi_{\widetilde{j}} + \omega_N^{\widetilde{j}} \Psi_{\widetilde{j}},\\ | ||
Linia 162: | Linia 161: | ||
</pre>}} | </pre>}} | ||
Jak już zdążyliśmy się przyzwyczaić, w algorytmach numerycznych unikamy | Jak już zdążyliśmy się przyzwyczaić, gdy tylko to możliwe, w algorytmach numerycznych unikamy | ||
stosowania jawnej rekurencji | stosowania jawnej rekurencji. W przypadku FFT można jej | ||
również uniknąć, wyznaczając zawczasu --- korzystając z tzw. odwrócenia bitów | również uniknąć, wyznaczając zawczasu --- korzystając z tzw. odwrócenia bitów | ||
--- porządek, w którym należy składać 1-wymiarowe DFT w coraz dłuższe wektory | --- porządek, w którym należy składać 1-wymiarowe DFT w coraz dłuższe wektory | ||
Linia 179: | Linia 178: | ||
Dla danych węzłów <math>\displaystyle x_k = \frac{2\pi k }{N}</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, znaleźć | Dla danych węzłów <math>\displaystyle x_k = \frac{2\pi k }{N}</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, znaleźć | ||
wielomian <math>\displaystyle P</math> (zmiennej rzeczywistej | wielomian <math>\displaystyle P</math> (zmiennej rzeczywistej o wartościach zespolonych) stopnia <math>\displaystyle \leq N-1</math> postaci | ||
<center><math>\displaystyle | <center><math>\displaystyle | ||
P(x) = \sum_{k=0}^{N-1} \beta_k \bar{\omega}^k, | P(x) = \sum_{k=0}^{N-1} \beta_k \bar{\omega}^k, | ||
Linia 210: | Linia 209: | ||
{{dowod||| | {{dowod||| | ||
Jak wiemy, <math>\displaystyle \frac{1}{N} F_N^* F_N = I</math> | Jak wiemy, <math>\displaystyle \frac{1}{N} F_N^* F_N = I</math>, z symetrii macierzy <math>\displaystyle F_N</math> wynika więc, | ||
że <math>\displaystyle (\bar{F_N})^{-1} = \frac{1}{N} F_N</math>. | że <math>\displaystyle (\bar{F_N})^{-1} = \frac{1}{N} F_N</math>. | ||
}} | }} | ||
Można pokazać, że gdy dane są rzeczywiste, zadanie interpolacji | Można pokazać, że gdy dane są rzeczywiste, zadanie interpolacji | ||
trygonometrycznej | trygonometrycznej możemy wyrazić | ||
korzystając wyłącznie z liczb rzeczywistych. | korzystając wyłącznie z liczb rzeczywistych. | ||
Jeśli <math>\displaystyle y_k\in R</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, to wtedy (rzeczywisty) wielomian trygonometryczny | Jeśli <math>\displaystyle y_k\in R</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, to wtedy (rzeczywisty) wielomian trygonometryczny | ||
Linia 229: | Linia 228: | ||
W niektórych zastosowaniach potrzebne jest wyznaczenie <strong>splotu</strong> dwóch | W niektórych zastosowaniach potrzebne jest wyznaczenie <strong>splotu</strong> dwóch | ||
wektorów, to znaczy | wektorów, to znaczy wyznaczenie wyrażeń postaci | ||
<center><math>\displaystyle | <center><math>\displaystyle | ||
z_k = \sum_{j=0}^{N-1} u_j v_{k-j}, \qquad k = 0, \ldots, N-1. | z_k = \sum_{j=0}^{N-1} u_j v_{k-j}, \qquad k = 0, \ldots, N-1. | ||
Linia 243: | Linia 242: | ||
\vdots & \ddots & \ddots & \vdots \\ | \vdots & \ddots & \ddots & \vdots \\ | ||
v_1 & \cdots & v_{N-1} & v_0 | v_1 & \cdots & v_{N-1} & v_0 | ||
\end{pmatrix} \cdot \begin{pmatrix} u_0\\u_1\\\vdots\\u_{N-1}\end{pmatrix} | \end{pmatrix} \cdot \begin{pmatrix} u_0\\u_1\\\vdots\\u_{N-1}\end{pmatrix} . | ||
</math></center> | </math></center> | ||
Tak więc pozornie zadanie wyznaczenia splotu powinno kosztować tyle, co | |||
mnożenie macierzy przez wektor, a więc <math>\displaystyle O(N^2)</math> operacji. Tymczasem | mnożenie macierzy przez wektor, a więc <math>\displaystyle O(N^2)</math> operacji. Tymczasem | ||
prosty rachunek pozwala sprawdzić, że odpowiednie transformacje Fouriera, | prosty rachunek pozwala sprawdzić, że odpowiednie transformacje Fouriera, | ||
Linia 269: | Linia 268: | ||
Najpopularniejszą obecnie biblioteką implementującą algorytm FFT dla DFT, DCT i | Najpopularniejszą obecnie biblioteką implementującą algorytm FFT dla DFT, DCT i | ||
innych pokrewnych (bez ograniczenia, że wymiar jest naturalną potęgą dwójki), | innych pokrewnych (bez ograniczenia, że wymiar jest naturalną potęgą dwójki), | ||
jest [http://www.fftw.org FFTW] której nazwa jest skrótowcem od niezbyt | jest [http://www.fftw.org FFTW], której nazwa jest skrótowcem od niezbyt | ||
skromnie brzmiącego ang. The Fastest Fourier Transform in the West. Z tej | skromnie brzmiącego ang. The Fastest Fourier Transform in the West. Z tej | ||
biblioteki korzystają m.in. funkcje MATLABa i Octave'a <code>fft</code> oraz | biblioteki korzystają m.in. funkcje MATLABa i Octave'a <code>fft</code> oraz | ||
Linia 277: | Linia 276: | ||
FFTW jest napisana w C i w dużym stopniu wykorzystuje możliwości współczesnych | FFTW jest napisana w C i w dużym stopniu wykorzystuje możliwości współczesnych | ||
procesorów, takie jak potokowanie i instrukcje wektorowe SSE2 i SSE3. Poniżej | procesorów, takie jak potokowanie i instrukcje wektorowe SSE2 i SSE3. Poniżej | ||
pokazujemy przykładowy prościutki kod w C | pokazujemy przykładowy prościutki kod w C realizujący DFT na pojedynczym | ||
wektorze zespolonym. | wektorze zespolonym. | ||
Linia 332: | Linia 331: | ||
Zwrócmy uwagę na linię <code>Plan=fftw_plan_dft_1d(...)</code>. To tutaj dokonywane są | Zwrócmy uwagę na linię <code>Plan=fftw_plan_dft_1d(...)</code>. To tutaj dokonywane są | ||
ustalenia, w jakiej kolejności mają być prowadzone obliczenia. Jest to operacja | ustalenia, w jakiej kolejności mają być prowadzone obliczenia. Jest to operacja | ||
dość kosztowna, dlatego jeśli mamy wyznaczyć wiele takich samych DFT | dość kosztowna, dlatego jeśli mamy wyznaczyć wiele takich samych DFT tylko na | ||
różnych danych, należy przed pierwszą DFT taki plan zachować, a potem już gotowy | różnych danych, należy przed pierwszą DFT taki plan zachować, a potem już gotowy | ||
wykorzystać, gdy chcemy aplikować DFT dla następnych danych. | wykorzystać, gdy chcemy aplikować DFT dla następnych danych. |
Wersja z 16:01, 25 wrz 2006
Szybka transformacja Fouriera (FFT)
Algorytm FFT dla dyskretnej transformacji Fouriera (DFT) i jego krewniacy dla wyznaczania dyskretnej transformacji cosinusów (DCT i MDCT), choć dotyczą pozornie dość abstrakcyjnych zadań, zrewolucjonizowały wiele dziedzin życia. Między innymi wykorzystuje się je w
- kompresji obrazów w formacie JPEG (DCT)
- kompresji dźwięku w formacie MP3 i pokrewnych (MDCT)
- rozwiązywaniu ważnych równań różniczkowych cząstkowych (DFT)
a także do
- fitrowania szumów (DFT)
- szybkiego mnożenia wielomianów (DFT)
W niniejszym wykładzie ograniczymy się do przedstawienia szybkiego algorytmu rozwiązywania zadania DFT. Algorytmy rozwiązywania zadań pokrewnych (DCT, MDCT, itp.) opierają się na podobnych zasadach.
Zacznijmy od postawienia zadania obliczeniowego DFT. Jest ono (pozornie!) jednocześnie banalne i wydumane:
Dla danego zestawu liczb , , wyznacz wartości
dla , przy czym . Jak pamiętamy, jednostka urojona spełnia . Taką operację nazywamy dyskretną transformacją Fouriera, DFT.

Zobacz biografię
Ponieważ dane są wektorem , wynik też jest wektorem, a zadanie jest liniowe, możemy wszystko zapisać macierzowo:
gdzie
Tak więc, gdyby naiwnie podejść do zadania, moglibyśmy je rozwiązać brutalnie, tworząc na początek macierz , a następnie wyznaczając iloczyn macierzy przez wektor, co łatwo zrobić kosztem operacji. Dlatego istotne jest, że algorytm FFT, który za chwilę omówimy, będzie działać kosztem , czyli praktycznie liniowym (w obu przypadkach stałe proporcjonalności są równe 2) w dodatku będzie miał znacznie mniejsze wymagania pamięciowe. Już nawet gdy (przypadek kompresji JPEG), mamy , gdy tymczasem , więc zysk jest ponaddwudziestokrotny, co ma kapitalne znaczenie, bo zamiast czekać na zapis zdjęcia (powiedzmy) pół sekundy, czekalibyśmy długie 10 (sic!) sekund... albo nasza(?) cyfrówka kosztowałaby majątek...
Fakt
Macierz jest symetryczna oraz .
Algorytm FFT
Zauważmy, że nasza macierz ma jeszcze więcej specjalnej struktury, powtarza się w niej bardzo wiele takich samych współczynników (sprawdź dla , w ogólności ma tylko różnych wyrazów), gdyż (dla , to nic innego jak wszystkie zespolone pierwiastki z jedynki), a jak wiadomo, mnożenie przez 1 nic nie kosztuje, o ile tylko się wie, gdzie jest ta jedynka. Z grubsza na tym właśnie będzie polegać siła algorytmu FFT!
W wyprowadzeniu algorytmu szybkiej transformacji Fouriera, Fast Fourier Transform, FFT oprzemy się po raz kolejny na regule "dziel i rządź". Dla uproszczenia analizy przyjmiemy, że jest naturalną potęgą dwójki, w szczególności dla pewnego naturalnego .
Rzeczywiście, rozbijając naszą sumę na sumę po indeksach parzystych i sumę po indeksach nieparzystych, mamy
Suma po indeksach parzystych da się zapisać w postaci
i analogicznie suma po indeksach nieparzystych da się zapisać
gdyż , a więc wygląda na to, że nasze zadanie wyznaczenia dyskretnej transformacji Fouriera wymiaru da się sprowadzić do analogicznych zadań mniejszego rozmiaru.
Rzeczywiście, korzystając z tego, że , gdzie , oraz lub , mamy .
Oznaczając
(jak widać są to DFT dla dwa razy krótszych wektorów złożonych z tylko parzystych lub tylko nieparzystych współrzędnych ), dostajemy ostatecznie
Tym samym, wyznaczenie DFT wymiaru udało się rzeczywiście sprowadzić do wyznaczenia dwóch DFT wymiaru oraz drobnej manipulacji na ich wynikach zgodnie z powyższym wzorem. Oczywiście, te mniejsze transformacje można wyznaczyć takim samym sposobem, co prowadzi do zależności rekurencyjnej, która kończy się na wektorach długości 1, na których DFT to po prostu identyczność.
Proste sprawdzenie pokazuje, że koszt takiego algorytmu jest rzędu , a nie , jak dla naiwnego algorytmu mnożenia wektora przez gęstą macierz. Zysk jest więc, nawet dla niewielkich , istotny.
Algorytm Prosta wersja algorytmu FFT
function y = fft(x) N = length(f); if N == 1 y = x; else <math>\displaystyle \omega</math> = <math>\displaystyle \exp(-\frac{2\Pi}{N}i)</math>; <math>\displaystyle \omega_k</math> = <math>\displaystyle \omega^{N/2-1}</math>; u = fft( f[0:2:N-2] ); v = fft( f[1:2:N-1] ); v = v * <math>\displaystyle \omega_k</math>; y = [ u+v ; u-v ]; end end
Jak już zdążyliśmy się przyzwyczaić, gdy tylko to możliwe, w algorytmach numerycznych unikamy stosowania jawnej rekurencji. W przypadku FFT można jej również uniknąć, wyznaczając zawczasu --- korzystając z tzw. odwrócenia bitów --- porządek, w którym należy składać 1-wymiarowe DFT w coraz dłuższe wektory zgodnie ze wzorem powyżej tak, by na końcu dostać pożądany wektor .
Ponadto, istnieją warianty algorytmu FFT, które np. działają na danych rzeczywistych. Na analogicznych zasadach co FFT oparte są również algorytmy wykonujące tzw. dyskretną transformację cosinusów (DCT) i jej wariant MDCT stosowany w kodekach audio takich jak MP3, AAC, czy OggVorbis.
Interpolacja trygonometryczna
Jednym z wielu zadań, w których daje się zastosować algorytm FFT, jest zadanie interpolacji trygonometrycznej:
Dla danych węzłów , , znaleźć wielomian (zmiennej rzeczywistej o wartościach zespolonych) stopnia postaci
gdzie (stąd nazwa: trygonometryczny) taki, że
dla zadanych wartości .
W języku macierzy DFT możemy zapisać zadanie interpolacji trygonometrycznej jako zadanie wyznaczania wektora takiego, że
dla zadanego wektora .
Twierdzenie
Współczynniki poszukiwanego wielomianu trygonometrycznego wyrażają się wzorem
Dowód
Można pokazać, że gdy dane są rzeczywiste, zadanie interpolacji trygonometrycznej możemy wyrazić korzystając wyłącznie z liczb rzeczywistych. Jeśli , , to wtedy (rzeczywisty) wielomian trygonometryczny
gdzie , , interpoluje w węzłach . Oczywiście, powyższa formuła w rzeczywistości ma o połowę mniej wyrazów, ze względu na własności funkcji trygonometrycznych.
Splot
W niektórych zastosowaniach potrzebne jest wyznaczenie splotu dwóch wektorów, to znaczy wyznaczenie wyrażeń postaci
Zapisując to macierzowo, szukamy iloczynu wektora z cykliczną macierzą Toeplitza (macierz Toeplitza ma stałe wyrazy wzdłuż diagonali) wyznaczoną przez ,
Tak więc pozornie zadanie wyznaczenia splotu powinno kosztować tyle, co mnożenie macierzy przez wektor, a więc operacji. Tymczasem prosty rachunek pozwala sprawdzić, że odpowiednie transformacje Fouriera, , , spełniają równanie z macierzą diagonalną!
a to mnożenie daje się wykonać kosztem liniowym, tym samym całe zadanie daje się policzyć kosztem .
Biblioteki
Najpopularniejszą obecnie biblioteką implementującą algorytm FFT dla DFT, DCT i
innych pokrewnych (bez ograniczenia, że wymiar jest naturalną potęgą dwójki),
jest FFTW, której nazwa jest skrótowcem od niezbyt
skromnie brzmiącego ang. The Fastest Fourier Transform in the West. Z tej
biblioteki korzystają m.in. funkcje MATLABa i Octave'a fft
oraz
ifft
dla transformacji DFT (to znaczy mnożenia przez ) i,
odpowiednio, transformacji odwrotnej, .
FFTW jest napisana w C i w dużym stopniu wykorzystuje możliwości współczesnych procesorów, takie jak potokowanie i instrukcje wektorowe SSE2 i SSE3. Poniżej pokazujemy przykładowy prościutki kod w C realizujący DFT na pojedynczym wektorze zespolonym.
/* Kompilacja: gcc -o dft dft.c -lfftw3 -lm */ #include <complex.h> /* rozszerzenie GCC dające operacje na typach zespolonych */ #include <fftw3.h> #include <math.h> #define N 8 int main(void) { fftw_complex *F, *C; fftw_plan Plan; double normfactor = 1.0/N; int i; F = fftw_malloc( N * sizeof(fftw_complex) ); C = fftw_malloc( N * sizeof(fftw_complex) ); for( i = 0; i < N; i++ ) /* inicjalizacja wartości tablicy F */ { F[i] = i*M_PI*(1-0.5*I); } Plan = fftw_plan_dft_1d( N, F, C, FFTW_FORWARD, FFTW_ESTIMATE ); fftw_execute( Plan ); for( i = 0; i < N; i++ ) /* normalizacja wyznaczonego C */ C[i] *= normfactor; for( i = 0; i < N; i++ ) { printf("F[%d] = %8.3lf + %8.3lfi \PIPEREAD C[%d] = %8.3lf + %8.3lfi\n", i, creal(F[i]), cimag(F[i]), i, creal(C[i]), cimag(C[i])); } /* .... teraz moglibyśmy zmienić wartości F i ponownie wyznaczyć C, korzystając z tego samego Planu! */ /* sprzątamy */ fftw_destroy_plan( Plan ); fftw_free( F ); fftw_free( C ); return(0); }
Zwrócmy uwagę na linię Plan=fftw_plan_dft_1d(...)
. To tutaj dokonywane są
ustalenia, w jakiej kolejności mają być prowadzone obliczenia. Jest to operacja
dość kosztowna, dlatego jeśli mamy wyznaczyć wiele takich samych DFT tylko na
różnych danych, należy przed pierwszą DFT taki plan zachować, a potem już gotowy
wykorzystać, gdy chcemy aplikować DFT dla następnych danych.