FFT
FFT有关的知识点总结。
前置知识
多项式求导
根据 $(x^n)’=nx^{n-1}$ 可推出。
多项式不定积分
根据 $\int x^ndx=\frac{x^{n+1}}{n+1} +C$ 可推出。
多项式加法
对应系数相加。
多项式减法
对应系数相减。
多项式乘法
DFT与IDFT的原理
给定两个多项式 $A(x)=\sum_{i=0}^na_ix^i,B(x)=\sum_{i=0}^nb_ix^i$,则两个多项式相乘后是:$(A * B)(x)=(a_0b_0)+(a_1b_0+a_0b_1)x+\dots+(a_nb_n)x^{2n}$
一个 $n$ 次多项式 $f(x)=\sum_{i=0}^na_ix^i$ 可以由 $n+1$ 个点 $(x_i,f(x_i))$ 唯一表示,那么FFT就是把两个多项式 $A(x),B(x)$(假设系数都为 $n$,不够最高位补0即可)的系数表示法转成由大于 $2n$ 个(因为相乘后多项式的次数是 $2n$的,要用大于 $2n$ 个点值才能表示)相同的点值 (即$(x_i,A(x_i)),(x_i,B(x_i))$)表示,然后对应函数值相乘,得到点值:$(x_i,A(x_i) \times B(x_i))$,最后再把点值表示换成系数表示的过程。
如果随便取一些 $x_i$,那么每次操作是 $O(n^2)$ 的,毫无卵用。。。
假设我们要取 $n$ 个点值(不妨设 $n$ 为偶数)。
考虑 $x_k$ 为方程 $x^n=1$ 的复数解,即 $x_k=\omega _ n ^ k = e ^ {\frac{2k\pi i}{n}}=\cos(\frac{2k\pi}{n})+i\sin(\frac{2k\pi}{n})$
取 $1$ 的 $n$ 次单位根的作用在于,这个 $\omega _ n ^ k$ 有如下性质:
1,$\omega _ n^k=\omega _ n^{k \bmod n}$
2,若 $n,k$ 为偶数,则 $\omega _ n^k=\omega _ {n/2}^{k/2}$
证明都十分显然。
我们先回到原问题上面,现在要求的是 $f(\omega _ n^0),f(\omega _ n^1)…f(\omega _ n^{n-1})$,考虑分治,把多项式中次数为偶数的系数放在 $f_0$ 中,奇数的放在 $f_1$ 中,即:
$$f_0(x)=\sum_{i=0}^{2i\leq n}a_{2i}x^i\f_1(x)=\sum_{i=0}^{2i+1\leq n}a_{2i+1}x^i$$
那么有:$f(\omega _ n^k)=f_0((\omega _ n^k)^2)+(\omega _ n^k)f_1((\omega _ n^k)^2)$
又因为 $(\omega _ n^k)^2=\omega _ n^{2k}=\omega _ {n/2}^k$
所以:$$f(\omega _ n^k)=f_0(\omega _ {n/2}^k)+(\omega _ n^k)f_1((\omega _ {n/2}^k))\ (0\leq k < n)$$
当 $0\leq k < \frac{n}{2} $ 时,有:
$$f(\omega _ n^k)=f_0(\omega _ {n/2}^k)+(\omega _ n^k)f_1((\omega _ {n/2}^k))\f(\omega _ n^{k+\frac{n}{2}})=f_0(\omega _ {n/2}^k)-(\omega _ n^k)f_1((\omega _ {n/2}^k))$$
这样就变成了两个子问题,递归求解即可。时间复杂度 $O(n\log n)$
现在我们已经将多项式用系数表示转成点值表示(dft)了,那么这个操作的逆变换(idft)怎么做呢?
考虑dft的过程,可以变成矩阵相乘的形式:
$\begin{bmatrix}
f(\omega _ n^0)\
f(\omega 1)\
f(\omega 2) \
\vdots\
f(\omega _ n^{n-1})
\end{bmatrix}
\begin{bmatrix}
1 & 1 & 1 & \dots & 1\
1 & \omega _ n^1 & \omega _ n^2 & \dots &\omega _ n^{n-1} \
1 & \omega _ n^2 & \omega _ n^4 & \dots & \omega _ n^{2(n-1)}\
\vdots & \vdots & \vdots & \ddots & \vdots\
1 & \omega _ n^{n-1} & \omega _ n^{2(n-1)} & \dots & \omega _ n^{(n-1)(n-1)}
\end{bmatrix}
\begin{bmatrix}
a_0\
a_1\
a_2\
\vdots\
a_{n-1}
\end{bmatrix}\$
将上述表达式从左到右的矩阵记为 $Y,V,A$,则有 $Y=VA$。
因为是逆过程,所以要找到 $V$ 的逆矩阵使得 $V^{-1}Y=A$.
可以发现我也不知道是怎么发现的 ,下面这个矩阵就是 $V$ 的逆矩阵:
$V^{-1}
=\frac{1}{n}
\begin{bmatrix}
1 & 1 & 1 & \dots & 1\
1 & \omega _ n^{-1} & \omega _ n^{-2} & \dots &\omega _ n^{-(n-1)} \
1 & \omega _ n^{-2} & \omega _ n^{-4} & \dots & \omega _ n^{-2(n-1)}\
\vdots & \vdots & \vdots & \dots & \vdots\
1 & \omega _ n^{-(n-1)} & \omega _ n^{-2(n-1)} & \dots & \omega _ n^{-(n-1)(n-1)}
\end{bmatrix}
$
证明的话…可以考虑暴力矩阵乘法:$C_{ij}=\sum_{k}V_{ik}V^{-1} _ {kj}$,把两个矩阵暴力乘起来后可以发现当 $i=j$ 时,$C_{ii}$ 的值为 $n$ ,否则为 $0$.
那么idft的过程就与dft的类似了。
所以只需要将序列分别dft一下,然后对应相乘,最后idft回去就可以啦~
到现在为止我们能做一个递归版的 fft 啦!
但时实测这样的速度非常慢,需要写非递归的。
考虑 fft 的过程,经过一堆分治后,系数 $a_i$最终会到达一个位置上,而这些位置跟 $i$ 有关,如果 $i$ 是偶数就会被分到 $f_0(x)$ 中(即左边),否则会被分到右边,因此 $i$ 最后的位置是将 $i$ 在二进制表示下将位翻转后的位置,预处理即可。
到现在为止我们就会做一个非递归版的 fft 啦!
NTT
那如果要对大质数取模呢?
我们直接跑FFT,最后取个模不就可以了?
但是FFT会有比较大的精度问题,当数字比较大的时候很容易挂掉。
考虑这个质数的一个原根 $g$,它的性质与 $n$ 次单位根相似,即 $g^{P-1} \equiv 1(\bmod p)$,但因为我们必须要保证每次做 dft 时次数 $n$ 必须是偶数,所以一开始的 $n$ 必须是2的次幂。所以要满足:$2^k | (P-1)$。
如当 $P=998244353$ 时, $k$ 最大是 $23$,即 $n$ 最大是 $8388608$。这就是快速数论变换(NTT)了。此时需满足 $P$ 为质数,且满足 $2^k|(P-1)$ 中最大的 $k$ 要满足 $n\leq 2^k$。
能跑NTT的一些模数有:$998244353,1004535809,469762049$ 等,这三个数的原根都有 $3$。
到现在为止我们就会做一个非递归版的对模数有限制的 ntt 啦!
一个小模板:
1 |
|
任意模数FFT
那如果模数不满足上面的限制呢?
这时有两种方法:
三模数FFT
先拿几个能做NTT的模数跑一次 NTT,然后用中国剩余定理合并。
显然选三个比较大的模数就可以了。
拆系数FFT
设 $m=\left \lfloor \sqrt{P} \right \rfloor$,将FFT中每个数表示成 $bm+a$ 的形式,且 $a<m$。那么就可以将两个数的乘法拆成四部分:$(bm+a)(dm+c)=bdm^2+(bc+ad)m+ac$。然后分别做普通的 FFT ,这样就避免了精度问题。此时一共需要 $4$ 次DFT,$3$ 次IDFT。常数大到爆炸。
考虑优化DFT,将两次dft合并到一次。
假设现在要求 $dft(a)$ 与 $dft(b)$。
设 $f_k=a_k+ib_k,g_k=a_k-ib_k$。
将有 $dft(f)k=\text{conj}(dft(g){n-k})$。其中 $\text{conj}$ 表示共轭。
证明:$dft(f)k=\sum{j=0}^{n-1} w_n^{kj}f_j=\sum_{j=0}^{n-1} \text{conj}(w_n^{-kj}g_j)=\sum_{j=0}^{n-1} w_n^{kj}\text{conj}(g_j)=\text{conj}(dft(g)_{n-k})$。
于是 $dft(a)_k=\frac{dft(f)_k+dft(g)_k}{2}$ ,$dft(b)_k=-i\times \frac{dft(f)_k-dft(g)_k}{2}$。
这样做的前提条件显然是 $a,b$ 中的数均为实数。
考虑优化IDFT,设 $h=dft(a)+i\times dft(b)$。
那么 $idft(h)$ 的实部即为 $a$,虚部即为 $b$。
于是就优化成了分别 $2$ 次的DFT和IDFT了。
模板:
1 | const ll mo=1e9+7; |
一些技能
多项式求逆
给一个$n$次多项式$A(x)$,求$B(x)$满足$A(x)* B(x)\equiv 1\left ( \bmod x^n \right )$
不妨设$n$为2的次幂:
当$n=1$时,求一个整数的逆就好了。
当$n\geq 2$时,
假设已经求出了$A(x)* C(x)\equiv 1\left ( \bmod x^{\frac{n}{2}} \right )$
$\because A(x)* B(x)\equiv 1\left ( \bmod x^n \right )$
$\therefore A(x)* B(x)\equiv 1\left ( \bmod x^{\frac{n}{2}} \right )$
$\therefore A(x)* (B(x)-C(x))\equiv 0\left ( \bmod x^{\frac{n}{2}} \right )$
又$\because A(x)\not\equiv 0\left ( \bmod x^n \right )$
$\therefore (B(x)-C(x))\equiv 0\left ( \bmod x^{\frac{n}{2}} \right )$
仔细想一想可以发现$\left (B(x)-C(x)\right )^2\equiv 0\left ( \bmod x^{n} \right )$
化简得$B^{2}(x)+C^{2}(x)-2B(x)C(x)\equiv 0\left ( \bmod x^{n} \right )$
同时乘上$A(x)$得:$B(x)+A(x)* C^{2}(x)-2C(x)\equiv 0\left ( \bmod x^{n} \right )$
移项得:$B(x)\equiv C(x)\left ( 2-A(x)* C(x)\right )\left ( \bmod x^{n} \right )$
多项式乘法+倍增即可。
时间复杂度$T(n)=T(\frac{n}{2})+O(n\log n)=O(n\log n)$
多项式除法
给两个次数分别为$n$和$m$的多项式$F(x),G(x)$,求$Q(x),R(x)$满足$F(x)=G(x)* Q(x)+R(x)$,且$Q(x)$的次数为$n-m$
$\because F(x)\equiv G(x)* Q(x)+R(x)\left ( \bmod x^{n+1}\right )$
$\therefore F(\frac{1}{x})\equiv G(\frac{1}{x})* Q(\frac{1}{x})+R(\frac{1}{x})\left ( \bmod x^{n+1}\right )$
同时乘以$x^n$得:$ F^{rev}(x)\equiv G^{rev}(x)* Q^{rev}(x)+R^{rev}(x)\times x^{n-dR}\left ( \bmod x^{n+1}\right )$(其中$rev$表示将多项式系数翻转,$dR$表示$R$这个多项式的次数,其余同理)
$\because dR < m $
$\therefore n-dR>n-m$
因此在$\bmod x^{n-m+1}$意义下,$R^{rev}(x)\times x^{n-dR}\equiv 0$
$\therefore F^{rev}(x)\equiv G^{rev}(x)* Q^{rev}(x) \left ( \bmod x^{n-m+1}\right )$
多项式求逆+翻转即可。
时间复杂度$O(n\log n)$
多项式取模
求出除法以后,取模变得非常简单。
时间复杂度$O(n\log n)$
多项式开方
给一个$n$次多项式$A(x)$,求$B(x)$满足$A(x)\equiv B^{2}(x)\left ( \bmod x^n \right )$
还是考虑倍增:
不妨设$n$为2的次幂:
当$n=1$时,求$x^2\equiv a\left ( \bmod p\right )$的解就好了。比较多的题目会保证常数项为1.
当$n\geq 2$时,
假设已经求出了$A(x)\equiv C^2(x)\left ( \bmod x^{\frac{n}{2}} \right )$,
易得$\left ( C(x)-B(x)\right ) * \left ( C(x)+B(x)\right )\equiv 0\left ( \bmod x^{\frac{n}{2}} \right )$
因此会有两个解,设$C(x)-B(x)\equiv 0\left ( \bmod x^{\frac{n}{2}} \right )$,则有$ C(x)+B(x)\equiv 0\left ( \bmod x^{\frac{n}{2}} \right )$
所以$ \left ( C(x)+B(x)\right )^2\equiv 0\Rightarrow C^2(x)+B^2(x)+2B(x)* C(x)\equiv 0\Rightarrow B(x)\equiv-\frac{C^2(x)+A(x)}{2C(x)}\left ( \bmod x^{n} \right )$
多项式求逆+乘法+倍增即可。
时间复杂度$T(n)=T(\frac{n}{2})+O(n\log n)=O(n\log n)$
多项式ln
为啥多项式可以求ln啊。。。无法理解。。。
先来看看泰勒展开:
$$e^x=\sum_{i=0}^{\infty}\frac{x^i}{i!}$$
那么多项式求ln实际上就是知道$G(x)=e^{F(x)}$,让你求$F(x)$的过程。
对等式两边同时求$ln$得$\ln(G(x))=F(x)$
求导得:$\frac{G’(x)}{G(x)}=F’(x)$
求积分得:$\int {\frac{G’(x)}{G(x)}}=F(x)$
没了。。。
时间复杂度$O(n\log n)$
多项式exp
能求ln那当然能求exp啦。
$F(x)=e^{A(x)}$
求ln得:$\ln F(x)=A(x)$
$\ln F(x)-A(x)=0$
考虑牛顿迭代:
$F_{i+1}(x)=F_i(x)-\frac{\ln F_{i}(x)-A(x)}{\left (\ln F_{i}(x)-A(x) \right )’}=F_{i}(x)-\frac{\ln F_{i}(x)-A(x)}{\frac{1}{F_{i}(x)}}=F_{i}(x)\left ( 1-\ln F_{i}(x)+A(x)\right )\left ( \bmod x^n \right )$
每次迭代 $n$ 会翻一倍,时间复杂度$T(n)=T(\frac{n}{2})+O(n\log n)=O(n\log n)$
多项式求幂
$F(x)=A^{k}(x)$
两边同时取对数得:$\ln F(x)=k \ln A(x)$
同时取指数得:$F(x)=\exp\left ( k\ln A(x)\right )$
时间复杂度 $O(n\log n)$
多项式三角函数
不是很清楚这种东西有什么用…
求 $\sin(A),\cos(A)$ :
直接硬上欧拉公式:
$$e^{ix}=\cos(x)+i\sin(x)\ e^{-ix}=\cos(x)-i\sin(x)$$
解得:
$$\cos(x)=\frac{e^{ix}+e^{-ix}}{2}\\sin(x)=\frac{e^{ix}-e^{-ix}}{2}$$
但是在模意义这个 $i$ 该怎么办呢?
考虑 $i^2=-1$,所以在模 $mod$ 的意义下这个 $i$ 就是 $mod-1$ 的二次剩余。
多点求值
“详见-多点求值”多点插值
常系数齐次线性递推
“详见”模板
1 |
|