文章目录
前言
Fast Fast TLE
原本做大数乘法的时候是想偷懒的, 就百度了下大数 A * B 的代码, 无意中发现有使用FFT的做法, 于是便开始了学习 (受苦) , 本文用以记录仅在算法应用层面上我对FFT的理解和一个手搓的大数乘法模版.
需要一定的复数知识, 但不多.
笔者对FFT的理解较为粗浅, 因此在下文可能大量略过某些部分的详细证明, 请见谅!
一、FFT是什么?
快速傅里叶变换( Fast Fourier Transform ), 是一种对离散傅里叶变换( Discrete Fourier Transform )的优化, 可令离散傅里叶变换的运算量大大降低, 在取样点数量较多的情况下可显著提升效率
二、FFT可以干什么?
1.多项式乘法
实际上, FFT可以大大提升卷积1的计算效率, 而当卷积的两个函数为多项式的时候, FFT则能在多项式乘法上大显神威.
2.大数乘法
关于乘法, 朴素的算法则是模拟我们自小学会的竖式乘法, 不难证明这样的算法的复杂度为O(n2), 然而这样的算法不仅容易精度溢出, 效率也不够高, 应用FFT则可以使算法的复杂度降低至O(nlogn).
观察一个数字, 如123456, 换种角度思考, 可以将其转化为以下的式子: 123456 = 1 ∗ 1 0 5 + 2 ∗ 1 0 4 + 3 ∗ 1 0 3 + 4 ∗ 1 0 2 + 5 ∗ 1 0 1 + 6 ∗ 1 0 0 123456 = 1 *10^5+2*10^4+3*10^3+4*10^2+5*10^1+6*10^0 123456=1∗105+2∗104+3∗103+4∗102+5∗101+6∗100再转化一次, 令x = 10, 则可以得到: 1 ∗ x 5 + 2 ∗ x 4 + 3 ∗ x 3 + 4 ∗ x 2 + 5 ∗ x 1 + 6 ∗ x 0 1 *x^5+2*x^4+3*x^3+4*x^2+5*x^1+6*x^0 1∗x5+2∗x4+3∗x3+4∗x2+5∗x1+6∗x0
从这个角度思考, 大数乘法不正是x = 10的情况下的多项式乘法吗?
三、FFT怎么做?
PS:强烈建议您通过这个视频进行学习并在需要的情况下结合下文理解, 个人认为这个视频讲得相当易懂
1. 系数表示法和点值表示法
任取一个n-1阶2 多项式, 将其表示为 a 0 + a 1 x + a 2 x 2 + . . . + a n − 1 x n − 1 a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1} a0+a1x+a2x2+...+an−1xn−1实际上我们已经得到了它的系数表示, 即 [ a 0 a 1 a 2 . . . a n − 1 ] \left[ \begin{array}{l} a_0&a_1&a_2&...&a_{n-1} \end{array} \right] [a0a1a2...an−1]
那么什么是它的点值表示法呢?
对一个多项式 f ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n − 1 x n − 1 f(x)= a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1} f(x)=a0+a1x+a2x2+...+an−1xn−1任取不小于n个xi带入并计算得到f(xi)即为其点值表示法:
{ ( x 0 , f ( x 0 ) , ( x 1 , f ( x 1 ) , … , ( x n − 1 , f ( x n − 1 ) } \left\{ (x_0,f(x_0),(x_1,f(x_1), \dots,(x_{n-1},f(x_{n-1}) \right\} {
(x0,f(x0),(x1,f(x1),…,(xn−1,f(xn−1)}
将其化为矩阵形式, 有
[ f ( x 1 ) f ( x 2 ) ⋮ f ( x − 1 ) ] = [ 1 x 0 x 0 2 … x 0 n − 1 1 x 1 x 1 2 … x 1 n − 1 … … … ⋮ … 1 x n − 1 x n − 1 2 … x n − 1 n − 1 ] [ a 0 a 1 ⋮ a n − 1 ] \left[ \begin{array}{l} f(x_1)\\ f(x_2)\\ \vdots \\ f(x-1) \end{array} \right] = \left[ \begin{array}{l} 1&x_0&x_0^2&\dots&x_{0}^{n-1}\\ 1&x_1&x_1^2&\dots&x_{1}^{n-1}\\ \dots&\dots&\dots&\vdots&\dots\\ 1&x_{n-1}&x_{n-1}^2&\dots&x_{n-1}^{n-1} \end{array} \right] \left[ \begin{array}{l} a_0\\ a_1\\ \vdots \\ a_{n-1} \end{array} \right] ⎣⎢⎢⎢⎡f(x1)f(x2)⋮f(x−1)⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡11…1x0x1…xn−1x02x12…xn−12……⋮…x0n−1x1n−1…xn−1n−1⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥⎥⎤
PS:从 系数表示法 得到 点值表示法 的这一步即为DFT(离散傅里叶变换)
由范德蒙德行列式易证得当x0 ~ xn-1互不相同时矩阵可逆, 即当所取的xi不小于矩阵的阶加一时, 点值表示法和系数表示法一一对应.
令 h ( x ) = f ( x ) g ( x ) h(x)=f(x)g(x) h(x)=f(x)g(x)
在点值表示法的基础上, 要计算h(x)的表达式, 只需要取相同的xi得出f(xi)和g(xi)并相乘, 再从(xi ,f(xi)g(xi))的点值表示法转换成系数表示法即可得到h(x)的值.
那么如何从点值表示法转换成系数表示法呢? 仔细观察上面的矩阵表达式不难得出:
{ ( x 0 , f ( x 0 ) , ( x 1 , f ( x 1 ) , … , ( x n − 1 , f ( x n − 1 ) } \left\{ (x_0,f(x_0),(x_1,f(x_1), \dots,(x_{n-1},f(x_{n-1}) \right\} { (x0,f(x0),(x1,f(x1),…,(xn−1,f(xn−1)}
将其化为矩阵形式, 有
[ a 0 a 1 ⋮ a n − 1 ] = [ 1 x 0 x 0 2 … x 0 n − 1 1 x 1 x 1 2 … x 1 n − 1 … … … ⋮ … 1 x n − 1 x n − 1 2 … x n − 1 n − 1 ] − 1 [ f ( x 1 ) f ( x 2 ) ⋮ f ( x − 1 ) ] \left[ \begin{array}{l} a_0\\ a_1\\ \vdots \\ a_{n-1} \end{array} \right] = \left[ \begin{array}{l} 1&x_0&x_0^2&\dots&x_{0}^{n-1}\\ 1&x_1&x_1^2&\dots&x_{1}^{n-1}\\ \dots&\dots&\dots&\vdots&\dots\\ 1&x_{n-1}&x_{n-1}^2&\dots&x_{n-1}^{n-1} \end{array} \right]^{-1} \left[ \begin{array}{l} f(x_1)\\ f(x_2)\\ \vdots \\ f(x-1) \end{array} \right] ⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥