Zaawansowane algorytmy i struktury danych/Wykład 4: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sank (dyskusja | edycje)
Sank (dyskusja | edycje)
Nie podano opisu zmian
Linia 1: Linia 1:
== Abstrakt ==
== Abstrakt ==


Naturalna metoda dodawania dwóch wielomianów wymaga czasu
Naturalna metoda dodawania dwóch wielomianów wymaga czasu <math>\Theta(n)</math>, natomiast prosty algorytm mnożenia dwóch wielomianów stopnia <math>n</math> wymaga czasu <math>\Theta(n^2)</math>. W wykładzie tym pokażemy, jak z wykorzystaniem szybkiej transformaty Fouriera (STF), wykonać wszystkie podstawowe operacje na wielomianach w czasie większym niż <math>\Theta(n)</math> o czynnik polilogarytmiczny. Na wykładzie pokażemy jak dla wielomianów stopnia <math>n</math>:
<math>\Theta(n)</math>, natomiast prosty algorytm mnożenia dwóch
wielomianów stopnia <math>n</math> wymaga czasu
<math>\Theta(n^2)</math>. W wykładzie tym pokażemy, jak z
wykorzystaniem szybkiej transformaty Fouriera (STF), wykonać wszystkie
podstawowe operacje na wielomianach w czasie większym niż
<math>\Theta(n)</math> o czynnik polilogarytmiczny. Na wykładzie pokażemy jak dla
wielomianów stopnia <math>n</math>:
* mnożyć je w czasie <math>O(n \log n)</math>,
* mnożyć je w czasie <math>O(n \log n)</math>,
* dzielić wielomiany w czasie <math>O(n \log n)</math>.
* dzielić wielomiany w czasie <math>O(n \log n)</math>.


Natomiast jako ćwiczenie zostanie nam pokazanie jak wykorzystać te algorytmy do  
Natomiast jako ćwiczenie zostanie nam pokazanie jak wykorzystać te algorytmy do
* obliczania wielomianu interpolacyjnegi w czasie <math>O(n \log^2 n)</math>,
* obliczania wielomianu interpolacyjnegi w czasie <math>O(n \log^2 n)</math>,
* obliczania wartości wielomianu w <math>n</math> punktach w czasie <math>O(n \log^2 n)</math>.
* obliczania wartości wielomianu w <math>n</math> punktach w czasie <math>O(n \log^2 n)</math>.
Linia 18: Linia 11:
== Mnożenie wielomianów w punktach ==
== Mnożenie wielomianów w punktach ==


Niech <math>A(x) = \sum_{i=0}^{n-1} a_i x^i</math> i
Niech <math>A(x) = \sum_{i=0}^{n-1} a_i x^i</math> i <math>B(x) = \sum_{i=0}^{n-1} b_i x^i</math> będą wielomianami stopnia <math>n</math> nad ciałem <math>F</math>. Wielomiany te możemy jednoznaczne reprezentować poprzez ich wartości w <math>n</math> punktach. Następujące twierdzenie zostało sformułowane w ramach wykładu z Metod Numerycznych.
<math>B(x) = \sum_{i=0}^{n-1} b_i x^i</math> będą
wielomianami stopnia <math>n</math> nad ciałem <math>F</math>.
Wielomiany te możemy jednoznaczne reprezentować poprzez ich
wartości w <math>n</math> punktach. Następujące twierdzenie zostało
sformułowane w ramach wykładu z Metod Numerycznych.


{{twierdzenie|[Twierdzenie o interpolacji
{{twierdzenie|[Twierdzenie o interpolacji wielomianów]|interpolacja|Dla dowolnego zbioru <math>n</math> par <math>X = \{(x_0, y_0), (x_1, y_1), \ldots, (x_{n-1},y_{n-1})\}</math> takiego, że wszystkie wartości <math>x_i</math> są parami różne, istnieje jedyny wielomian <math>C(x)</math> stopnia <math>n</math> taki, że <math>C(x_i) = y_i</math> dla <math>i = 0,1,\ldots, n-1.</math>
wielomianów]|interpolacja|Dla dowolnego zbioru <math>n</math> par <math>X = \{(x_0, y_0), (x_1, y_1), \ldots, (x_{n-1},y_{n-1})\}</math> takiego, że wszystkie wartości <math>x_i</math> są parami różne, istnieje jedyny wielomian <math>C(x)</math> stopnia <math>n</math> taki, że <math>C(x_i) = y_i</math> dla <math>i = 0,1,\ldots, n-1.</math>
}}
}}




Niech <math>X</math> będzie ustalonym zbiorem parami różnych punktów
Niech <math>X</math> będzie ustalonym zbiorem parami różnych punktów <math>x_0, \ldots, x_{2n-1} \in F</math>. Dla tego zbioru punktów możemy wyznaczyć zbiory wartości wielomianów:
<math>x_0, \ldots, x_{2n-1} \in F</math>. Dla tego zbioru punktów
możemy wyznaczyć zbiory wartości wielomianów:




Linia 42: Linia 27:




Niech <math>C</math> będzie wynikiem mnożenia
Niech <math>C</math> będzie wynikiem mnożenia wielomianów <math>A</math> i <math>B</math>, mamy wtedy
wielomianów <math>A</math> i <math>B</math>, mamy wtedy




Linia 51: Linia 35:




Ponieważ stopień wielomianu <math>C</math> jest nie większy niż
Ponieważ stopień wielomianu <math>C</math> jest nie większy niż <math>2n</math> to z [[ZASD Moduł 4#interpolacja| Twierdzenia o interpolacji]] zbiór wartości
<math>2n</math> to z [[ZASD Moduł 4#interpolacja| Twierdzenia o interpolacji]] zbiór wartości




<center>
<center>
<math>X_{A\times B} = \{(x_0, A(x_0)B(x_0)), (x_1, A(x_1)B(x_1),
<math>X_{A\times B} = \{(x_0, A(x_0)B(x_0)), (x_1, A(x_1)B(x_1), \ldots, (2x_{n-1}, A(x_{2n-1})B(x_{2n-1})) \}</math>,
\ldots, (2x_{n-1}, A(x_{2n-1})B(x_{2n-1})) \}</math>,
</center>
</center>


jednoznacznie wyznacza wielomian <math>A \times B</math>. Mając
jednoznacznie wyznacza wielomian <math>A \times B</math>. Mając zbiory <math>X_A</math> i <math>X_B</math> możemy wyznaczyć zbiór <math>X_C</math> w czasie <math>O(n)</math>. Procedura ta jest przedstawiona na następującym rysunku:
zbiory <math>X_A</math> i <math>X_B</math> możemy wyznaczyć zbiór
<math>X_C</math> w czasie <math>O(n)</math>. Procedura ta jest
przedstawiona na następującym rysunku:




Linia 72: Linia 51:




 
Jednak aby ostatecznie otrzymać szybszy algorytm niż algorytm naiwny musimy pokazać jak rozwiązać problem obliczania wartości wielomianu w <math>n</math> punktach w czasie szybszym niż <math>\Theta(n^2)</math>. Podobnie musimy umieć obliczać wielomian interpolacyjny dla danego zbioru punktów.
 
 
 
 
 
 
Jednak aby ostatecznie otrzymać szybszy algorytm niż algorytm naiwny
musimy pokazać jak rozwiązać problem obliczania wartości wielomianu
w <math>n</math> punktach w czasie szybszym niż
<math>\Theta(n^2)</math>. Podobnie musimy umieć obliczać wielomian
interpolacyjny dla danego zbioru punktów.


== Szybka transformata Fouriera (STF) ==
== Szybka transformata Fouriera (STF) ==
Linia 112: Linia 80:


Widzimy teraz, że problem ewaluacji wielomianu <math>A(x)</math> w punktach <math>\omega_n^0, \omega_n^1,\ldots, \omega_n^{n-1}</math> sprowadza się do:
Widzimy teraz, że problem ewaluacji wielomianu <math>A(x)</math> w punktach <math>\omega_n^0, \omega_n^1,\ldots, \omega_n^{n-1}</math> sprowadza się do:
* ewaluacji wielomianów <math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math>
* ewaluacji wielomianów <math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math> w punktach
w punktach




Linia 131: Linia 98:




Możemy teraz zauważyć, że zachodzi <math>x_i^2 = x_{i+\frac{n}{2}}^2</math>, a więc <math>X' =
Możemy teraz zauważyć, że zachodzi <math>x_i^2 = x_{i+\frac{n}{2}}^2</math>, a więc <math>X' = X_{\frac{n}{2}}</math>. Udało nam się więc zredukować problem rozmiaru <math>n</math> - obliczenia wartości wielomianu <math>A(x)</math> stopnia <math>n</math> w <math>n</math> punktach, do dwóch problemów rozmiaru <math>\frac{n}{2}</math> - obliczenia wartości wielomianów <math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math> stopnia <math>\frac{n}{2}</math> w <math>\frac{n}{2}</math> punktach. Możemy teraz zastosować tą technikę rekurencyjne otrzymując następujący algorytm.
X_{\frac{n}{2}}</math>. Udało nam się więc zredukować problem rozmiaru <math>n</math> - obliczenia wartości wielomianu <math>A(x)</math> stopnia <math>n</math> w <math>n</math> punktach, do dwóch problemów rozmiaru <math>\frac{n}{2}</math> - obliczenia wartości wielomianów <math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math> stopnia <math>\frac{n}{2}</math> w <math>\frac{n}{2}</math> punktach. Możemy teraz zastosować tą technikę rekurencyjne otrzymując następujący algorytm.


{{algorytm|Algorytm Szybkiej Transformaty Fouriera|algorytm_fft|
{{algorytm|Algorytm Szybkiej Transformaty Fouriera|algorytm_fft|
Linia 154: Linia 120:
}}
}}


Algorytm ten najpierw oblicza SFT wielomianów
Algorytm ten najpierw oblicza SFT wielomianów <math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math> a następnie łączy te wyniki w celu wyliczenia SFT dla wielomianu <math>A(x)</math>. Przeanalizujmy teraz wykonanie pętli. Zauważmy najpierw, że w <math>k</math>'tym  kroku pętli mamy <math>\omega = \omega_n^k = e^{\frac{2\pi i k}{n}} = x_k.</math>. Czyli:
<math>A^{[0]}(x)</math> i <math>A^{[1]}(x)</math> a następnie łączy
te wyniki w celu wyliczenia SFT dla wielomianu <math>A(x)</math>.
Przeanalizujmy teraz wykonanie pętli. Zauważmy najpierw, że w
<math>k</math>'tym  kroku pętli mamy <math>\omega = \omega_n^k
= e^{\frac{2\pi i k}{n}} = x_k.</math>. Czyli:




<center><math>
<center><math>
y_k = y_k^{[0]} + x_k y_k^{[1]} = A^{[0]}(e^{\frac{2\pi i k}{n/2}}) +
y_k = y_k^{[0]} + x_k y_k^{[1]} = A^{[0]}(e^{\frac{2\pi i k}{n/2}}) + x_k A^{[1]}(e^{\frac{2\pi i k}{n/2}}) =
x_k A^{[1]}(e^{\frac{2\pi i k}{n/2}}) =
</math></center>
</math></center>
<center><math>
<center><math>
= A^{[0]} \left(\left(e^{\frac{2\pi i k}{n}}\right)^2\right) +
= A^{[0]} \left(\left(e^{\frac{2\pi i k}{n}}\right)^2\right) + x_k A^{[1]}\left(\left(e^{\frac{2\pi i k}{n}}\right)^2\right) = A^{[0]}(x_k^2) + x_k A^{[1]}(x_k^2) = A(x_k),
x_k A^{[1]}\left(\left(e^{\frac{2\pi i k}{n}}\right)^2\right)
= A^{[0]}(x_k^2) +
x_k A^{[1]}(x_k^2) = A(x_k),
</math></center>
</math></center>


Linia 178: Linia 135:


<center><math>
<center><math>
y_{k+\frac{n}{2}} = y_k^{[0]} - x_k y_k^{[1]}
y_{k+\frac{n}{2}} = y_k^{[0]} - x_k y_k^{[1]} = A^{[0]}(e^{\frac{2\pi i k}{n/2}}) - e^{\frac{2\pi i k}{n}} A^{[1]}(e^{\frac{2\pi i k}{n/2}}) =
= A^{[0]}(e^{\frac{2\pi i k}{n/2}}) -
e^{\frac{2\pi i k}{n}} A^{[1]}(e^{\frac{2\pi i k}{n/2}}) =
</math></center>
</math></center>


<center><math>
<center><math>
= A^{[0]}(e^{\frac{2\pi i (k + n/2)}{n/2}}) +
= A^{[0]}(e^{\frac{2\pi i (k + n/2)}{n/2}}) + e^{\frac{2\pi i k + n/2}{n}} A^{[1]}(e^{\frac{2\pi i (k + n/2)}{n/2}}) = A^{[0]}(x_{k + n/2}^2) + x_{k + n/2}^2 A^{[1]}(x_{k + n/2}^2) = A(x_{k+\frac{n}{2}}).
e^{\frac{2\pi i k + n/2}{n}} A^{[1]}(e^{\frac{2\pi i (k + n/2)}{n/2}})
= A^{[0]}(x_{k + n/2}^2) +
x_{k + n/2}^2 A^{[1]}(x_{k + n/2}^2)
= A(x_{k+\frac{n}{2}}).
</math></center>
</math></center>


Gdzie w ostatniej równości skorzystaliśmy ze wzoru
Gdzie w ostatniej równości skorzystaliśmy ze wzoru [[ZASD Moduł 4#wzor_1|(1)]]. Widzimy zatem, że algorytm poprawnie oblicza wartość STF dla wielomianu <math>A(x)</math>. Równanie rekurencyjne na czas działania procedury STF wygląda następująco:
[[ZASD Moduł 4#wzor_1|(1)]]. Widzimy zatem, że algorytm poprawnie oblicza
wartość STF dla wielomianu <math>A(x)</math>. Równanie rekurencyjne
na czas działania procedury STF wygląda następująco:




Linia 203: Linia 151:


=== Odwrotna transformata Fouriera ===
=== Odwrotna transformata Fouriera ===
Aby zakończyć konstrukcję algorytmu dla szybkiego mnożenia
 
wielomianów pozostaje nam pokazanie jak obliczyć wielomian
Aby zakończyć konstrukcję algorytmu dla szybkiego mnożenia wielomianów pozostaje nam pokazanie jak obliczyć wielomian interpolujący dla zbioru punktów <math>X_n</math>. Obliczenie wykonane w czasie szybkiej transformaty Fouriera możemy przedstawić w postaci macierzowej jako mnożenie macierzy przez wektor <math>(A(x_0), A(x_1), \ldots, A(x_{n-1}))^T = V_n (a_0, a_1, \ldots, a_{n-1})^T</math>, gdzie <math>V_n = V(x_0,\ldots,x_{n-1})</math> jest macierzą Vandermonde'a zawierającą potęgi <math>x_j</math>
interpolujący dla zbioru punktów <math>X_n</math>. Obliczenie
wykonane w czasie szybkiej transformaty Fouriera możemy przedstawić
w postaci macierzowej jako mnożenie macierzy przez wektor
<math>(A(x_0), A(x_1), \ldots, A(x_{n-1}))^T = V_n (a_0, a_1,
\ldots, a_{n-1})^T</math>, gdzie <math>V_n = V(x_0,\ldots,x_{n-1})</math> jest macierzą
Vandermonde'a zawierającą potęgi <math>x_j</math>




Linia 263: Linia 205:
</center>
</center>


W celu wykonania operacji odwrotnej do SFT, czyli obliczenia wielomianu
W celu wykonania operacji odwrotnej do SFT, czyli obliczenia wielomianu interpolacyjnego, musimy wykonać mnożenie <math>V_n^{-1} (A(x_0), A(x_1), \ldots, A(x_{n-1}))^T</math>.
interpolacyjnego, musimy wykonać mnożenie
<math>V_n^{-1} (A(x_0), A(x_1), \ldots, A(x_{n-1}))^T</math>.


{{lemat|||
{{lemat|||Niech macierz <math>W_n</math> będzie zdefiniowana jako
Niech macierz <math>W_n</math> będzie zdefiniowana jako




Linia 278: Linia 217:
jest macierzą odwrotną do macierzy <math>V_n</math>.
jest macierzą odwrotną do macierzy <math>V_n</math>.
}
}
{{dowod|||
{{dowod|||Pokażemy, że <math>V_n W_n = I</math>. Rozważmy pozycję <math>(j,k)</math> macierzy <math>V_n W_n</math>:
Pokażemy, że <math>V_n W_n = I</math>. Rozważmy pozycję <math>(j,k)</math>
macierzy <math>V_n W_n</math>:




Linia 290: Linia 227:




Jeżeli <math>j=k</math> to <math>e^{\frac{2\pi k(j-k)}{n}} =
Jeżeli <math>j=k</math> to <math>e^{\frac{2\pi k(j-k)}{n}} = 1</math> i suma ta jest równa <math>1</math>. W przeciwnym przypadku możemy skorzystać ze wzoru na sumę szeregu geometrycznego:
1</math> i suma ta jest równa <math>1</math>. W przeciwnym przypadku
możemy skorzystać ze wzoru na sumę szeregu geometrycznego:




Linia 306: Linia 241:
}}
}}


Porównując postać macierzy <math>V_n</math> oraz macierzy <math>W_n</math>
Porównując postać macierzy <math>V_n</math> oraz macierzy <math>W_n</math> widzimy, że w celu obliczenia transformaty odwrotnej możemy użyć [[ZASD Moduł 4#algorytm_fft|Algorytmu Szybkiej Transformaty Fouriera]], musimy tylko zamienić linijkę <math>\omega_m = e^{\frac{2\pi i}{n}}</math> na <math>\omega_m = e^{-\frac{2\pi i}{n}}</math> i podzielić otrzymany wynik przez <math>n</math>.
widzimy, że w celu obliczenia transformaty odwrotnej możemy użyć
[[ZASD Moduł 4#algorytm_fft|Algorytmu Szybkiej Transformaty Fouriera]], musimy
tylko zamienić linijkę <math>\omega_m = e^{\frac{2\pi i}{n}}</math> na
<math>\omega_m = e^{-\frac{2\pi i}{n}}</math> i podzielić otrzymany wynik
przez <math>n</math>.


== Dzielenie wielomianów ==
== Dzielenie wielomianów ==


W tej części wykładu skupimy się na problemie dzielenia dwóch
W tej części wykładu skupimy się na problemie dzielenia dwóch wielomianów. Niech <math>A(x)</math> będzie wielomianem stopnia <math>m</math> a <math>B(x)</math> wielomianem stopnie <math>n</math>. Zakładamy bez straty ogólności, że <math>b_{n-1}\neq 0</math>. W problemie dzielenia wielomianów, chcemy obliczyć dwa wielomiany <math>D(x)</math> i <math>R(x)</math> takie, że
wielomianów. Niech <math>A(x)</math> będzie wielomianem stopnia
<math>m</math> a <math>B(x)</math> wielomianem stopnie
<math>n</math>. Zakładamy bez straty ogólności, że <math>b_{n-1}\neq
0</math>. W problemie dzielenia wielomianów, chcemy obliczyć dwa
wielomiany <math>D(x)</math> i <math>R(x)</math> takie, że


{{wzor|wzor_2|2|<math>A(x) = D(x) B(x)  + R(x),</math>}}
{{wzor|wzor_2|2|<math>A(x) = D(x) B(x)  + R(x),</math>}}


oraz stopień wielomianu <math>R(x)</math> jest ostro mniejszy niż
oraz stopień wielomianu <math>R(x)</math> jest ostro mniejszy niż <math>n</math>. Wielomian <math>D(x)</math> nazywamy {{def|wynikiem dzielenia|}, a wielomian <math>R(x)</math> to {{def|reszta z dzielenia|}. Pierwszy pomysł jaki się od razu nasuwa, to spróbować policzyć odwrotność wielomianu <math>B(x)</math> i przemnożenie przez tą odwrotność stronami tego równania. Niestety wielomiany nie mają niestety odwrotności będących wielomianami. Jednak nie pozostajemy tutaj zupełnie bezradni. Możemy rozszerzyć naszą dziedzinę obliczeń tak aby zagwarantować istnienie pewnych odwrotności.
<math>n</math>. Wielomian <math>D(x)</math> nazywamy {{def|wynikiem
dzielenia|}, a wielomian <math>R(x)</math> to {{def|reszta z
dzielenia|}. Pierwszy pomysł jaki się od razu nasuwa, to spróbować
policzyć odwrotność wielomianu <math>B(x)</math> i przemnożenie
przez tą odwrotność stronami tego równania. Niestety wielomiany nie
mają niestety odwrotności będących wielomianami. Jednak nie pozostajemy
tutaj zupełnie bezradni. Możemy rozszerzyć naszą dziedzinę obliczeń
tak aby zagwarantować istnienie pewnych odwrotności.


Obliczenia będziemy wykonywać nad zbiorem szeregów formalnych
Obliczenia będziemy wykonywać nad zbiorem szeregów formalnych <math>F[[x]]</math> nad ciałem <math>F</math>, patrz [[Matematyka_dyskretna#szeregi formalne| Wykład z matematyki dyskretnej]]. Dla części elementów <math>F[[x]]</math> istnieją odwrotności. Elementy te są postaci <math>a + x A(x)</math> gdzie <math>a\neq 0</math> i <math>A(x) \in F[[x]]</math>.
<math>F[[x]]</math> nad ciałem <math>F</math>, patrz
[[Matematyka_dyskretna#szeregi formalne| Wykład z matematyki
dyskretnej]]. Dla części elementów <math>F[[x]]</math> istnieją
odwrotności. Elementy te są postaci <math>a + x A(x)</math> gdzie
<math>a\neq 0</math> i <math>A(x) \in F[[x]]</math>.




Linia 366: Linia 278:




gdzie <math>A^R(z) = z^m A(\frac{1}{z})</math>, <math>D^R(z) =
gdzie <math>A^R(z) = z^m A(\frac{1}{z})</math>, <math>D^R(z) = z^{m-n} D(\frac{1}{z})</math>, <math>B^R(z) = z^{n} B(\frac{1}{z})</math> i <math>R^R(z) = z^{n-1} R(\frac{1}{z})</math>, oznaczają wielomiany otrzymane poprzez odwrócenie kolejności współczynników. Z założenia, że <math>b_{n-1}\neq 0 </math> wiemy, że wielomian <math>B^R</math> ma odwrotność nad zbiorem szeregów formalnych. Możemy zapisać więc:
z^{m-n} D(\frac{1}{z})</math>, <math>B^R(z) = z^{n}
B(\frac{1}{z})</math> i <math>R^R(z) = z^{n-1}
R(\frac{1}{z})</math>, oznaczają wielomiany otrzymane poprzez
odwrócenie kolejności współczynników. Z założenia, że
<math>b_{n-1}\neq 0 </math> wiemy, że wielomian <math>B^R</math> ma
odwrotność nad zbiorem szeregów formalnych. Możemy zapisać więc:




Linia 379: Linia 285:
</math></center>
</math></center>


Zauważmy, że w celu wyznaczenia <math>D^R(z)</math> potrzebujemy
Zauważmy, że w celu wyznaczenia <math>D^R(z)</math> potrzebujemy tylko <math>m-n-1</math> wyrazów szeregu <math>\left(B^R(z)\right)^{-1}</math>. Wyższe wyrazy i tak znikną w wyniku wykonania mnożenia modulo <math>z^{m-n-1}</math>. Pozostało nam teraz tylko pokazać jak wyznaczyć odwrotność dla szeregu formalnego. Algorytm ten przedstawiony jest poniżej
tylko <math>m-n-1</math> wyrazów szeregu
<math>\left(B^R(z)\right)^{-1}</math>. Wyższe wyrazy i tak znikną w
wyniku wykonania mnożenia modulo <math>z^{m-n-1}</math>. Pozostało
nam teraz tylko pokazać jak wyznaczyć odwrotność dla szeregu
formalnego. Algorytm ten przedstawiony jest poniżej




Linia 404: Linia 305:




a to jest równe wielokrotności <math>x^{2m}</math>, a więc jest
a to jest równe wielokrotności <math>x^{2m}</math>, a więc jest także wielokrotnością <math>x^{n}</math>. Jeżeli wykorzystamy szybkie mnożenie wielomianów do obliczenia <math>(A^{[0]}(x) -(A(x) A^{[0]}(x) -1)A^{[0]}(x))</math> to złożoność tego algorytmu wynosić będzie <math>O(n\log n)</math>. Możemy teraz skonstruować algorytm wykonujący dzielenie wielomianu <math>A(x)</math> przez wielomian <math>B(x)</math> w czasie <math>O(m \log m)</math>, gdzie <math>m</math> to stopień wielomianu <math>A(x)</math>.
także wielokrotnością <math>x^{n}</math>. Jeżeli wykorzystamy
szybkie mnożenie wielomianów do obliczenia <math>(A^{[0]}(x) -(A(x)
A^{[0]}(x) -1)A^{[0]}(x))</math> to złożoność tego algorytmu wynosić
będzie <math>O(n\log n)</math>. Możemy teraz skonstruować algorytm
wykonujący dzielenie wielomianu <math>A(x)</math> przez wielomian
<math>B(x)</math> w czasie <math>O(m \log m)</math>, gdzie
<math>m</math> to stopień wielomianu <math>A(x)</math>.





Wersja z 16:03, 1 sie 2006

Abstrakt

Naturalna metoda dodawania dwóch wielomianów wymaga czasu Θ(n), natomiast prosty algorytm mnożenia dwóch wielomianów stopnia n wymaga czasu Θ(n2). W wykładzie tym pokażemy, jak z wykorzystaniem szybkiej transformaty Fouriera (STF), wykonać wszystkie podstawowe operacje na wielomianach w czasie większym niż Θ(n) o czynnik polilogarytmiczny. Na wykładzie pokażemy jak dla wielomianów stopnia n:

  • mnożyć je w czasie O(nlogn),
  • dzielić wielomiany w czasie O(nlogn).

Natomiast jako ćwiczenie zostanie nam pokazanie jak wykorzystać te algorytmy do

  • obliczania wielomianu interpolacyjnegi w czasie O(nlog2n),
  • obliczania wartości wielomianu w n punktach w czasie O(nlog2n).

Mnożenie wielomianów w punktach

Niech A(x)=i=0n1aixi i B(x)=i=0n1bixi będą wielomianami stopnia n nad ciałem F. Wielomiany te możemy jednoznaczne reprezentować poprzez ich wartości w n punktach. Następujące twierdzenie zostało sformułowane w ramach wykładu z Metod Numerycznych.

Twierdzenie [Twierdzenie o interpolacji wielomianów]

Dla dowolnego zbioru n par X={(x0,y0),(x1,y1),,(xn1,yn1)} takiego, że wszystkie wartości xi są parami różne, istnieje jedyny wielomian C(x) stopnia n taki, że C(xi)=yi dla i=0,1,,n1.


Niech X będzie ustalonym zbiorem parami różnych punktów x0,,x2n1F. Dla tego zbioru punktów możemy wyznaczyć zbiory wartości wielomianów:


XA={(x0,A(x0)),(x1,A(x1),,(x2n1,A(x2n1))}

XB={(x0,B(x0)),(x1,B(x1),,(x2n1,B(x2n1))}


Niech C będzie wynikiem mnożenia wielomianów A i B, mamy wtedy


C(xi)=A(xi)B(xi).


Ponieważ stopień wielomianu C jest nie większy niż 2n to z Twierdzenia o interpolacji zbiór wartości


XA×B={(x0,A(x0)B(x0)),(x1,A(x1)B(x1),,(2xn1,A(x2n1)B(x2n1))},

jednoznacznie wyznacza wielomian A×B. Mając zbiory XA i XB możemy wyznaczyć zbiór XC w czasie O(n). Procedura ta jest przedstawiona na następującym rysunku:


<flash>file=Zasd_fft1.swf|width=460|height=350</flash>


Jednak aby ostatecznie otrzymać szybszy algorytm niż algorytm naiwny musimy pokazać jak rozwiązać problem obliczania wartości wielomianu w n punktach w czasie szybszym niż Θ(n2). Podobnie musimy umieć obliczać wielomian interpolacyjny dla danego zbioru punktów.

Szybka transformata Fouriera (STF)

Problem obliczania wartości wielomianu w n punktach i problem jego interpolacji rozwiążemy wykorzystując szybką transformatę Fouriera. W poprzednim rozdziale nie zakładaliśmy nic na temat zbioru punktów X. Głównym pomysłem w konstrukcji algorytmu STF będzie właśnie wybór odpowiedniego zbioru punktów X tak, aby jak największa ilość wykonywanych obliczeń powtarzała się.

Założymy, że chcemy obliczyć wartości wielomianu A(x)=i=0n1aixi oraz n jest parzyste. Jeżeli n jest nieparzyste to dodajemy na początek A(x) jednomian 0xn+1 co nie zmienia nam wyniku działania algorytmu. Punkty Xn={x0,x1,,xn1} zdefiniujemy w następujący sposób:


xi=e2πin.


Dla wielomianu A(x) definiujemy dwa nowe wielomiany A[0](x) i A[1](x) poprzez wybranie do nich współczynników A(x) o numerach odpowiednio parzystych i nieparzystych:


A[0](x)=a0+a2x+a4x2++an2xn21,

A[1](x)=a1+a3x+a5x2++an1xn21.


Wielomiany A[0](x) oraz A[1](x) są stopnia co najwyżej n2. Co więcej zachodzi:

A(x)=A[0](x2)+xA[1](x2)      (1)

Widzimy teraz, że problem ewaluacji wielomianu A(x) w punktach ωn0,ωn1,,ωnn1 sprowadza się do:

  • ewaluacji wielomianów A[0](x) i A[1](x) w punktach


X={x02,x12,,xn12}.


  • a następnie obliczenie wartości A(x)wyniku zgodnie ze wzorem (1).

Zauważmy, że z definicji punktów $x_i$ mamy:


xi2=(e2πin)2=e2πin/2.


Możemy teraz zauważyć, że zachodzi xi2=xi+n22, a więc X=Xn2. Udało nam się więc zredukować problem rozmiaru n - obliczenia wartości wielomianu A(x) stopnia n w n punktach, do dwóch problemów rozmiaru n2 - obliczenia wartości wielomianów A[0](x) i A[1](x) stopnia n2 w n2 punktach. Możemy teraz zastosować tą technikę rekurencyjne otrzymując następujący algorytm.

Algorytm Algorytm Szybkiej Transformaty Fouriera


 STF(a=(a0,,an1))
 if n nieparzyste then
   dodaj wyraz an do a
   zwiększ n
 if n=1 then return a
 ωn=e2πin
 ω=1
 a[0]=(a0,a2,,an2)
 a[1]=(a1,a3,,an1)
 y[0]=SFT(a[0])
 y[1]=SFT(a[1])
 for k=0 to n21 do
   yk=yk[0]+ωyk[1]
   yk+n2=yk[0]ωyk[1]
   \omega = \omega \omega_n
 return y

Algorytm ten najpierw oblicza SFT wielomianów A[0](x) i A[1](x) a następnie łączy te wyniki w celu wyliczenia SFT dla wielomianu A(x). Przeanalizujmy teraz wykonanie pętli. Zauważmy najpierw, że w k'tym kroku pętli mamy ω=ωnk=e2πikn=xk.. Czyli:


yk=yk[0]+xkyk[1]=A[0](e2πikn/2)+xkA[1](e2πikn/2)=
=A[0]((e2πikn)2)+xkA[1]((e2πikn)2)=A[0](xk2)+xkA[1](xk2)=A(xk),


oraz


yk+n2=yk[0]xkyk[1]=A[0](e2πikn/2)e2πiknA[1](e2πikn/2)=
=A[0](e2πi(k+n/2)n/2)+e2πik+n/2nA[1](e2πi(k+n/2)n/2)=A[0](xk+n/22)+xk+n/22A[1](xk+n/22)=A(xk+n2).

Gdzie w ostatniej równości skorzystaliśmy ze wzoru (1). Widzimy zatem, że algorytm poprawnie oblicza wartość STF dla wielomianu A(x). Równanie rekurencyjne na czas działania procedury STF wygląda następująco:


T(n)=2T(n2)+Θ(n)=Θ(nlogn).


Odwrotna transformata Fouriera

Aby zakończyć konstrukcję algorytmu dla szybkiego mnożenia wielomianów pozostaje nam pokazanie jak obliczyć wielomian interpolujący dla zbioru punktów Xn. Obliczenie wykonane w czasie szybkiej transformaty Fouriera możemy przedstawić w postaci macierzowej jako mnożenie macierzy przez wektor (A(x0),A(x1),,A(xn1))T=Vn(a0,a1,,an1)T, gdzie Vn=V(x0,,xn1) jest macierzą Vandermonde'a zawierającą potęgi xj


(A(x0)A(x1)A(x2)A(xn1))=[x00x01x02x03x0n1x10x11x12x13x1n1x20x21x22x23x2n1xn10xn11xn12xn13xn1n1](a0a1a2an1)


Element macierzy V(x0,,xn1) dany jest jako


(Vn)j,k=V(x0,,xn1)j,k=xjk.


Korzystając z definicji zbioru Xn otrzymujemy


V(x0,,xn1)j,k=(e2πijn)k=e2πijkn.

W celu wykonania operacji odwrotnej do SFT, czyli obliczenia wielomianu interpolacyjnego, musimy wykonać mnożenie Vn1(A(x0),A(x1),,A(xn1))T.

{{lemat|||Niech macierz Wn będzie zdefiniowana jako


(Wn)j,k=1ne2πijkn,


jest macierzą odwrotną do macierzy Vn. } Dowód

Pokażemy, że VnWn=I. Rozważmy pozycję (j,k) macierzy VnWn:


(VnWn)j,k=l=0n1(Vn)j,l(Wn)l,k=l=0n1e2πijln1ne2πilkn=l=0n11ne2πl(jk)n=


Jeżeli j=k to e2πk(jk)n=1 i suma ta jest równa 1. W przeciwnym przypadku możemy skorzystać ze wzoru na sumę szeregu geometrycznego:


=1n1e2πn(jk)n1e2π(jk)n=1n11(jk)1e2π(jk)n=0.


Czyli rzeczywiście VnWn=I. \qed

Porównując postać macierzy Vn oraz macierzy Wn widzimy, że w celu obliczenia transformaty odwrotnej możemy użyć Algorytmu Szybkiej Transformaty Fouriera, musimy tylko zamienić linijkę ωm=e2πin na ωm=e2πin i podzielić otrzymany wynik przez n.

Dzielenie wielomianów

W tej części wykładu skupimy się na problemie dzielenia dwóch wielomianów. Niech A(x) będzie wielomianem stopnia m a B(x) wielomianem stopnie n. Zakładamy bez straty ogólności, że bn10. W problemie dzielenia wielomianów, chcemy obliczyć dwa wielomiany D(x) i R(x) takie, że

A(x)=D(x)B(x)+R(x),      (2)

oraz stopień wielomianu R(x) jest ostro mniejszy niż n. Wielomian D(x) nazywamy {{def|wynikiem dzielenia|}, a wielomian R(x) to {{def|reszta z dzielenia|}. Pierwszy pomysł jaki się od razu nasuwa, to spróbować policzyć odwrotność wielomianu B(x) i przemnożenie przez tą odwrotność stronami tego równania. Niestety wielomiany nie mają niestety odwrotności będących wielomianami. Jednak nie pozostajemy tutaj zupełnie bezradni. Możemy rozszerzyć naszą dziedzinę obliczeń tak aby zagwarantować istnienie pewnych odwrotności.

Obliczenia będziemy wykonywać nad zbiorem szeregów formalnych F[[x]] nad ciałem F, patrz Wykład z matematyki dyskretnej. Dla części elementów F[[x]] istnieją odwrotności. Elementy te są postaci a+xA(x) gdzie a0 i A(x)F[[x]].


Ćwiczenie odwrotność_formalna

{{{3}}}

Ćwiczenie odwrotność_formalna

{{{3}}}


Do wzoru (2) wstawmy x=1z otrzymamy wtedy:


AR(z)=DR(z)BR(z)+zmn1RR(z)=BR(z)DR(z)modzmn1,


gdzie AR(z)=zmA(1z), DR(z)=zmnD(1z), BR(z)=znB(1z) i RR(z)=zn1R(1z), oznaczają wielomiany otrzymane poprzez odwrócenie kolejności współczynników. Z założenia, że bn10 wiemy, że wielomian BR ma odwrotność nad zbiorem szeregów formalnych. Możemy zapisać więc:


DR(z)=AR(x)(BR(z))1modzmn1.

Zauważmy, że w celu wyznaczenia DR(z) potrzebujemy tylko mn1 wyrazów szeregu (BR(z))1. Wyższe wyrazy i tak znikną w wyniku wykonania mnożenia modulo zmn1. Pozostało nam teraz tylko pokazać jak wyznaczyć odwrotność dla szeregu formalnego. Algorytm ten przedstawiony jest poniżej


Algorytm Obliczanie pierwszych m wyrazów odwrotnośći szeregu formalnego


 ODWROTNOŚĆ(A(x) = \sum_{i=0}^{n-1} a_i x^i, m)
 if m=1 then return 1/a0
 A[0](x)=ODWROTNOŚĆ(A(x),m2)
 return (A[0](x)(A(x)A[0](x)1)A[0](x))modxm

Obliczenie to jest poprawne ponieważ:


1A(x)(A[0](x)(A(x)A[0](x)1)A[0](x))=A(x)(1A(x)A[0](x))2,


a to jest równe wielokrotności x2m, a więc jest także wielokrotnością xn. Jeżeli wykorzystamy szybkie mnożenie wielomianów do obliczenia (A[0](x)(A(x)A[0](x)1)A[0](x)) to złożoność tego algorytmu wynosić będzie O(nlogn). Możemy teraz skonstruować algorytm wykonujący dzielenie wielomianu A(x) przez wielomian B(x) w czasie O(mlogm), gdzie m to stopień wielomianu A(x).


Algorytm Algorytm dzielenia wielomianów


 PODZIEL(A(x), B(x))
  AR(z)=zmA(1z)
  BR(z)=znB(1z)
  (BR(z)1)(z)=ODWROTNOŚĆ(BR(z),mn1)
  DR(z)=AR(x)(BR(z))1modzmn1
  D(x)=zmn1DR(1x)
  R(x)=A(x)D(x)B(x)
  return (D(x),R(x))