You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Thus, if a direct DFT computes the values of the polynomial at the points at the $n$-th roots, the inverse DFT can restore the coefficients of the polynomial using those values.
35
35
36
-
## Application of the DFT: fast multiplication of polynomials
36
+
###Application of the DFT: fast multiplication of polynomials
37
37
38
38
Let there be two polynomials $A$ and $B$.
39
39
We compute the DFT for each of them: $\text{DFT}(A)$ and $\text{DFT}(B)$.
@@ -59,7 +59,7 @@ We can accomplish this by adding coefficients with the value $0$.
59
59
And also, since the result of the product of two polynomials is a polynomial of degree $2 (n - 1)$, we have to double the degrees of each polynomial (again by padding $0$s).
60
60
From a vector with $n$ values we cannot reconstruct the desired polynomial with $2n - 1$ coefficients.
61
61
62
-
## Fast Fourier Transform
62
+
###Fast Fourier Transform
63
63
64
64
The **fast Fourier transform** is a method that allows computing the DFT in $O(n \log n)$ time.
65
65
The basic idea of the FFT is to apply divide and conquer.
Thus we learned how to compute the DFT in $O(n \log n)$ time.
109
109
110
-
## Inverse FFT
110
+
###Inverse FFT
111
111
112
112
Let the vector $(y_0, y_1, \dots y_{n-1})$ - the values of polynomial $A$ of degree $n - 1$ in the points $x = w_n^k$ - be given.
113
113
We want to restore the coefficients $(a_0, a_1, \dots, a_{n-1})$ of the polynomial.
@@ -167,7 +167,7 @@ we notice that these problems are almost the same, so the coefficients $a_k$ can
167
167
168
168
Thus the computation of the inverse DFT is almost the same as the calculation of the direct DFT, and it also can be performed in $O(n \log n)$ time.
169
169
170
-
## Implementation
170
+
###Implementation
171
171
172
172
Here we present a simple recursive **implementation of the FFT** and the inverse FFT, both in one function, since the difference between the forward and the inverse FFT are so minimal.
173
173
To store the complex numbers we use the complex type in the C++ STL.
@@ -253,7 +253,7 @@ The only thing we have to do afterwards, is to normalize the number:
253
253
254
254
Since the length of the product of two numbers never exceed the total length of both numbers, the size of the vector is enough to perform all carry operations.
255
255
256
-
## Improved implementation: in-place computation
256
+
###Improved implementation: in-place computation
257
257
258
258
To increase the efficiency we will switch from the recursive implementation to an iterative one.
259
259
In the above recursive implementation we explicitly separated the vector $a$ into two vectors - the element on the even positions got assigned to one temporary vector, and the elements on odd positions to another.
@@ -481,6 +481,29 @@ The constants `mod`, `root`, `root_pw` determine the module and the root, and `r
481
481
482
482
In practice this implementation is slower than the implementation using complex numbers (due to the huge number of modulo operations), but it has some advantages such as less memory usage and no rounding errors.
483
483
484
+
## Multiplication with arbitrary modulus
485
+
486
+
Here we want to achieve the same goal as in previous section.
487
+
Multiplying two polynomial $A(x)$ and $B(x)$, and computing the coefficients modulo some number $M$.
488
+
The number theoretic transform only works for certain prime numbers.
489
+
What about the case when the modulus is not of the desired form?
490
+
491
+
One option would be to perform multiple number theoretic transforms with different prime numbers of the form $c 2^k + 1$, then apply the [Chinese Remainder Theorem](./algebra/chinese-remainder-theorem.html) to compute the final coefficients.
492
+
493
+
Another options is to distribute the polynomials $A(x)$ and $B(x)$ into two smaller polynomials each
494
+
$$\begin{align}
495
+
A(x) &= A_1(x) + A_2(x) \cdot C \\\\
496
+
B(x) &= B_1(x) + B_2(x) \cdot C
497
+
\end{align}$$
498
+
with $C \approx \sqrt{M}$.
499
+
500
+
Then the product of $A(x)$ and $B(x)$ can then be represented as:
The polynomials $A_1(x)$, $A_2(x)$, $B_1(x)$ and $B_2(x)$ contain only coefficients smaller than $\sqrt{M}$, therefore the coefficients of all the appearing products are smaller than $M \cdot n$, which is usually small enough to handle with typical floating point types.
504
+
505
+
This approach therefore requires computing the products of polynomials with smaller coefficients (by using the normal FFT and inverse FFT), and then the original product can be restored using modular addition and multiplication in $O(n)$ time.
506
+
484
507
## Applications
485
508
486
509
DFT can be used in a huge variety of other problems, which at the first glance have nothing to do with multiplying polynomials.
@@ -523,11 +546,53 @@ We want to find all ways to attach the first stripe to the second one, such that
523
546
The problem doesn't actually differ much from the previous problem.
524
547
Attaching two stripes just means that we perform a cyclic shift on the second array, and we can attach the two stripes, if scalar product of the two arrays is $0$.
525
548
549
+
### String matching
550
+
551
+
We are given two strings, a text $T$ and a pattern $P$, consisting of lowercase letters.
552
+
We have to compute all the occurrences of the pattern in the text.
553
+
554
+
We create a polynomial for each string ($T[i]$ and $P[I]$ are numbers between $0$ and $25$ corresponding to the $26$ letters of the alphabet):
Notice that with the expression $P[m-i-1]$ explicitly reverses the pattern.
565
+
566
+
The $(m-1+i)$th coefficients of the product of the two polynomials $C(x) = A(x) \cdot B(x)$ will tell us, if the pattern appears in the text at position $i$.
567
+
568
+
$$c_{m-1+i} = \sum_{j = 0}^{m-1} a_{i+j} \cdot b_{m-1-j} = \sum_{j=0}^{m-1} \left(\cos(\alpha_{i+j}) + i \sin(\alpha_{i+j})\right) \cdot \left(\cos(\beta_j) - i \sin(\beta_j)\right)
569
+
$$
570
+
with $\alpha_{i+j} = \frac{2 \pi T[i+j]}{26}$ and $\beta_j = \frac{2 \pi P[j]}{26}$
571
+
572
+
If there is a match, than $T[i+j] = P[j]$, and therefore $\alpha_{i+j} = \beta_j$.
573
+
This gives (using the Pythagorean trigonometric identity):
574
+
$$\begin{align}
575
+
c_{m-1+i} &= \sum_{j = 0}^{m-1} \left(\cos(\alpha_{i+j}) + i \sin(\alpha_{i+j})\right) \cdot \left(\cos(\alpha_{i+j}) - i \sin(\alpha_{i+j})\right) \\\\
If there isn't a match, then at least a character is different, which leads that one of the products $a_{i+1} \cdot b_{m-1-j}$ is not equal to $1$, which leads to the coefficient $c_{m-1+i} \ne m$.
580
+
581
+
### String matching with wildcards
582
+
583
+
This is an extension of the previous problem.
584
+
This time we allow that the pattern contains the wildcard character $ * $, which can match every possible letter.
585
+
E.g. the pattern $a*c$ appears in the text $abccaacc$ at exactly three positions, at index $0$, index $4$ and index $5$.
586
+
587
+
We create the exact same polynomials, except that we set $b_i = 0$ if $P[m-i-1] = *$.
588
+
If $x$ is the number of wildcards in $P$, then we will have a match of $P$ in $T$ at index $i$ if $c_{m-1+i} = m - x$.
0 commit comments