MN08
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 , , wyznaczyć wartości
dla , przy czym . Jak pamiętamy, jednostka urojona spełnia . Taką operację nazywamy dyskretną transformacją Fouriera, DFT.
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 .
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, że 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ę poraz 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
Efektywna implementacja FFT
Jak już zdążyliśmy się przyzwyczaić, w algorytmach numerycznych unikamy stosowania jawnej rekurencji, gdy tylko to możliwe. 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 Ogg-Vorbis..
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żna 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 | 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.
Interpolacja splajnowa
Interpolacja wielomianami interpolacyjnymi, chociaż korzysta z funkcji gładkich i łatwo reprezentowalnych w komputerze, ma jednak również pewne wady. Zauważmy, że błąd interpolacji może być bardzo duży (zjawisko Rungego), a poza tym, interpolacja jest nielokalna: nawet mała zmiana warości funkcji w pojedynczym węźle może powodować dużą zmianę zachowania całego wielomianu interpolacyjnego. Czasem więc lepiej jest zastosować innego rodzaju interpolację, np. posługując się funkcjami sklejanymi.
Co to są funkcje sklejane?
W ogólności przez funkcję sklejaną rozumie się każdą funkcję przedziałami wielomianową. Nas będą jednak interesować szczególne funkcje tego typu i dlatego termin funkcje sklejane zarezerwujemy dla funkcji przedziałami wielomianowych i posiadających dodatkowe własności, które teraz określimy.
Niech dany będzie przedział skończony i węzły
przy czym .
Definicja
Funkcję Parser nie mógł rozpoznać (nieznana funkcja „\toR”): {\displaystyle \displaystyle s:R\toR} nazywamy funkcją sklejaną rzędu () odpowiadającą węzłom , , jeśli spełnione są następujące dwa warunki:
- (i)
- jest wielomianem stopnia co najwyżej na każdym
z przedziałów , tzn. , ,
- (ii)
- jest -krotnie różniczkowalna w sposób
ciągły na całej prostej, tzn. .
Jeśli ponadto
- (iii)
- jest wielomianem stopnia co najwyżej poza
, tzn. ,
to jest naturalną funkcją sklejaną.
Klasę naturalnych funkcji sklejanych rzędu opartych na węzłach będziemy oznaczać przez , albo po prostu , jeśli węzły są ustalone.
Na przykład, funkcją sklejaną rzędu pierwszego () jest funkcja ciągła i liniowa na poszczególnych przedziałach . Jest ona naturalna, gdy poza jest funkcją stała. Tego typu funkcje nazywamy liniowymi funkcjami sklejanymi.
Najważniejszymi a praktycznego punktu widzenia są jednak funkcje sklejane rzędu drugiego odpowiadające . Są to funkcje, które są na dwa razy różniczkowalne w sposób ciągły, a na każdym z podprzedziałów są wielomianami stopnia co najwyżej trzeciego. W tym przypadku mówimy o kubicznych funkcjach sklejanych. Funkcja sklejana kubiczna jest naturalna, gdy poza jest wielomianem liniowym.
Interpolacja i gładkość
Pokażemy najpierw ważny lemat, który okaże się kluczem do dowodu dalszych twierdzeń.
Niech będzie klasą funkcji Parser nie mógł rozpoznać (nieznana funkcja „\toR”): {\displaystyle \displaystyle f:[a,b]\toR} takich, że jest razy różniczkowalna na w sposób ciągły oraz istnieje prawie wszędzie na i jest całkowalna z kwadratem, tzn.
Oczywiście każda funkcja sklejana rzędu (niekoniecznie naturalna) należy do .
Lemat
Niech będzie funkcją zerującą się w węzłach, tzn.
Wtedy dla dowolnej naturalnej funkcji sklejanej mamy
Dowód
Dla funkcja jest przedziałami stała. Oznaczając przez jej wartość na dostajemy
ponieważ zeruje się w .
Rozpatrzmy teraz przypadek . Ponieważ
to
Wobec tego, że jest poza przedziałem wielomianem stopnia co najwyżej oraz jest ciągła na , mamy , a stąd
Postępując podobnie, tzn. całkując przez części razy otrzymujemy w końcu
Funkcja jest przedziałami stała, a więc możemy teraz zastosować ten sam argument jak dla , aby pokazać,
że ostatnia całka jest równa zeru.
Funkcje sklejane chcielibyśmy zastosować do interpolacji funkcji. Ważne jest więc, aby odpowiednie zadanie interpolacyjne miało jednoznaczne rozwiązanie.
Twierdzenie
Jeśli , to dla dowolnej funkcji Parser nie mógł rozpoznać (nieznana funkcja „\toR”): {\displaystyle \displaystyle f:[a,b]\toR} istnieje dokładnie jedna naturalna funkcja sklejana interpolująca w węzłach , tzn. taka, że
Dowód
Pokażemy najpierw, że jedyną naturalną funkcją sklejaną interpolującą dane zerowe jest funkcja zerowa. Rzeczywiście, jeśli dla , to podstawiając w poprzednim Lemacie otrzymujemy
Stąd jest funkcją zerową, a więc jest wielomianem stopnia co najwyżej zerującym się w co najmniej punktach . Wobec tego, że , otrzymujemy .
Zauważmy teraz, że problem znalezienia naturalnej funkcji sklejanej interpolującej można sprowadzić do rozwiązania kwadratowego układu równań liniowych. Na każdym przedziale , , jest ona postaci
dla pewnych współczynników Parser nie mógł rozpoznać (nieznana funkcja „\inR”): {\displaystyle \displaystyle a_{i,j}\inR} , a na i mamy odpowiednio
i
Aby wyznaczyć , musimy więc znaleźć ogółem współczynników , przy czym są one związane warunkami jednorodnymi wynikającymi z gładkości,
dla i , oraz niejednorodnymi warunkami interpolacyjnymi,
dla . Otrzymujemy więc układ równań liniowych ze względu na niewiadomych .
Naturalna funkcja sklejana interpolująca jest wyznaczona jednoznacznie wtedy i tylko wtedy, gdy układ ten ma jednoznaczne rozwiązanie. To zaś zachodzi, gdy zero jest jedynym rozwiązaniem układu jednorodnego. Rzeczywiście, układ jednorodny odpowiada zerowym warunkom interpolacyjnym, przy których, jak pokazaliśmy wcześniej, zerowa funkcja sklejana (której odpowiada , )
jest jedynym rozwiązaniem zadania interpolacyjnego.
Naturalne funkcje sklejane możemy więc używać do interpolacji funkcji. Pokażemy teraz inną ich własność, która jest powodem dużego praktycznego zainteresowania funkcjami sklejanymi.
Twierdzenie
Niech i niech będzie naturalną funkcją sklejaną rzędu interpolującą w węzłach , . Wtedy
Dowód
Jeśli przedstawimy w postaci , to
Funkcja jest w klasie i zeruje się w węzłach , . Z Lematu wynika więc, że
, a stąd wynika teza.
Wartość całki może być w ogólności uważana za miarę gładkości funkcji. Dowiedzioną nierówność możemy więc zinterpretować w następujący sposób. Naturalna funkcja sklejana jest w klasie najgładszą funkcją spełniającą dane warunki interpolacyjne w wybranych węzłach .
Jak już wspomnieliśmy, najczęściej używanymi są kubiczne funkcje sklejane. Dlatego rozpatrzymy je oddzielnie.
Kubiczne funkcje sklejane
Jeśli zdecydowaliśmy się na użycie kubicznych funkcji sklejanych to powstaje problem wyznaczenia interpolującej daną funkcję , tzn. takiej, że dla . W tym celu, na każdym przedziale przedstawimy w postaci jej rozwinięcia w szereg Taylora w punkcie ,
i podamy algorytm obliczania dla .
Warunki brzegowe i warunki ciągłości dla dają nam oraz , czyli
gdzie . Stąd, przyjmując dodatkowo , otrzymujemy
Z warunków ciągłości dla dostajemy z kolei
oraz
Warunki ciągłości dają w końcu
Powyższe równania definiują nam na odcinku naturalną kubiczną funkcję sklejaną. Ponieważ poszukiwana funkcja sklejana ma interpolować , mamy dodatkowych warunków interpolacyjnych , , oraz , z których
Teraz możemy warunki ciągłości przepisać jako
przy czym wzór ten zachodzi również dla . Po wyrugowaniu i podstawieniu z (Uzupelnic: dei ), mamy
gdzie jest odpowiednią różnicą dzieloną. Możemy teraz powyższe wyrażenie na podstawić, aby otrzymać
Wprowadzając oznaczenie
możemy to równanie przepisać jako
, albo w postaci macierzowej
gdzie
Ostatecznie, aby znaleźć współczynniki należy najpierw rozwiązać układ równań liniowych, a potem zastosować wzory definiujące pozostałe współczynniki.
Zauważmy, że macierz układu równań liniowych jest trójdiagonalna i ma dominującą przekątną. Układ można więc rozwiązać kosztem proporcjonalnym do wymiaru używając algorytmu przeganiania. Koszt znalezienia wszystkich współczynników kubicznej funkcji sklejanej interpolującej jest więc też proporcjonalny do .
Na końcu oszacujemy jeszcze błąd interpolacji naturalnymi kubicznymi funkcjami sklejanymi na przedziale . Będziemy zakładać, że jest dwa razy różniczkowalna w sposób ciągły.
Twierdzenie
J eśli to
W szczególności, dla podziału równomiernego Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle x_i=a+(b-a)i/ń} , , mamy
Dowód
Wykorzystamy obliczoną wcześniej postać interpolującej funkcji sklejanej . Dla mamy
Z rozwinięcia w szereg Taylora w punkcie dostajemy oraz . Stąd
oraz
Niech teraz . Z postaci układu (Uzupelnic: ukltrd ) otrzymujemy
a stąd i z (Uzupelnic: psik )
co kończy dowód.

Zamiast terminu funkcje sklejane używa się też często terminów splajny (ang. spline-sklejać), albo funkcje gięte.
Niech
Ustalmy węzły . Dla , niech będzie naturalną funkcją sklejaną interpolującą w , , a dowolną inną aproksymacją korzystającą jedynie z informacji o wartościach w tych węzłach , tzn.
Załóżmy, że błąd aproksymacji mierzymy nie w normie Czebyszewa, ale w normie średniokwadratowej, zdefiniowanej jako
Wtedy
Aproksymacja naturalnymi funkcjami sklejanymi jest więc optymalna w klasie .
Można również pokazać, że interpolacja naturalnymi funkcjami sklejanymi na węzłach równoodległych Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle x_j=a+(b-a)j/ń} , , jest optymalna co do rzędu w klasie , wśród wszystkich aproksymacji korzystających jedynie z informacji o wartościach funkcji w dowolnych punktach, oraz
Tak jak wielomiany, naturalne funkcje sklejane interpolujące dane funkcje można reprezentować przez ich współczynniki w różnych bazach. Do tego celu można na przykład użyć bazy kanonicznej , , zdefiniowanej równościami
przy której . Baza kanoniczna jest jednak niewygodna w użyciu, bo funkcje w ogólności nie zerują się na żadnym podprzedziale, a tym samym manipulowanie nimi jest utrudnione.
Częściej używa się bazy B-sklejanej , . W przypadku funkcji kubicznych, , jest ona zdefiniowana przez następujące warunki:
Dla i dodatkowo żądamy, aby
a dla i podobnie
Wtedy nie zeruje się tylko na przedziale , Wyznaczenie współczynników rozwinięcia w bazie funkcji sklejanej interpolującej wymaga rozwiązania układu liniowego z macierzą trójdiagonalną , a więc koszt obliczenia tych współczynników jest proporcjonalny do .
Oprócz naturalnych funkcji sklejanych często rozpatruje się też okresowe funkcje sklejane. Są to funkcje Parser nie mógł rozpoznać (nieznana funkcja „\toR”): {\displaystyle \displaystyle \tilde s:R\toR} spełniające warunki (i), (ii) z Rozdziału Uzupelnic: warfs , oraz warunek:
- (iii)'
- jest dla
funkcją okresową o okresie , tzn. , .
Klasę okresowych funkcji sklejanych rzędu oznaczymy przez . Funkcje te mają podobne własności jak naturalne funkcje sklejane. Dokładniej, niech
tzn. jest klasą funkcji z , które można przedłużyć do funkcji, krórych wszystkie pochodne do rzędu włącznie są -okresowe na . Wtedy dla dowolnej funkcji zerującej się w węzłach , oraz dla dowolnej mamy
Jest to odpowiednik Lematu Uzupelnic: bwazny w przypadku okresowym. W szczególności wynika z niego jednoznaczność rozwiązania zadania interpolacyjnego dla okresowych funkcji (tzn. takich, że ), jak również odpowiednia własność minimalizacyjna okresowych funkcji sklejanych. Dokładniej, jeśli oraz interpoluje w węzłach , , to
Klasyczne zadanie aproksymacyjne w przestrzeniach funkcji definiuje się w następujący sposób.
Niech będzie pewną przestrzenią liniową funkcji Parser nie mógł rozpoznać (nieznana funkcja „\toR”): {\displaystyle \displaystyle f:[a,b]\toR} , w której określona została norma . Niech będzie podprzestrzenią w wymiaru . Dla danej , należy znaleźć funkcję taką, że
Okazuje się, że tak postawione zadanie ma rozwiązanie , choć nie zawsze jest ono wyznaczone jednoznacznie, zob. ćw. Uzupelnic: rkl .
Jako przykład, rozpatrzmy . Utożsamiając funkcje takie, że , zdefiniujemy w normę
Dla ustalonych węzłów , niech
będzie podprzestrzenią w naturalnych funkcji sklejanych rzędu opartych węzłach , . Oczywiście , co wynika z jednoznaczności rozwiązania w zadania interpolacji. Okazuje się, że wtedy optymalną dla jest naturalna funkcja sklejana interpolująca w węzłach , tzn.
Rzeczywiście, ponieważ norma w przestrzeni generowana jest przez iloczyn skalarny
jest to przestrzeń unitarna. Znane twierdzenie mówi, że w przestrzeni unitarnej najbliższą danej funkcją w dowolnej domkniętej podprzestrzeni jest rzut prostopadły na , albo równoważnie, taka funkcja , że iloczyn skalarny
W naszym przypadku, ostatnia równość jest równoważna
To zaś jest na mocy Lematu Uzupelnic: bwazny prawdą gdy interpoluje w punktach , czyli .
Dodajmy jeszcze, że nie zawsze interpolacja daje najlepszą aproksymację w sensie klasycznym, zob. ćw. Uzupelnic: intkla .