首页 > 其他分享 >深入理解 FFT

深入理解 FFT

时间:2023-02-15 20:46:02浏览次数:46  
标签:多项式 FFT cdots 理解 深入 点值 bmatrix omega vdots

理论前置

知道啥是多项式(即 \(f(x)=\displaystyle\sum_{i=0}^{n-1}f_ix^i\) 这一类东西)。

知道啥是多项式的卷积(即 \((f\times g)(x)=h(x)\),其中 \(h_i=\displaystyle\sum_{k=0}^if_kg_{i-k}\))。

知道啥是复数(即 \(a+bi\) 这种,其中 \(i^2=-1\))。

知道啥是单位根(即 \(x^n=1\) 的所有复数解,记作 \(\omega_n\))。

引入

随着 OI 中毒瘤出题人和毒瘤数数题的变多,我们有时候会需要求两个多项式的卷积,根据定义暴力计算,我们有一个 \(\mathcal{O}(n^2)\) 的优秀做法。

显然毒瘤出题人不会止步于此,于是我们需要更快的算法。

这时就需要引入多项式的另外一种表示方法了。

点值表示法

对于 \(f(x)\),我们不仅可以通过系数表示法(\(f(x)=\displaystyle\sum_{i=0}^{n-1}f_ix^i\))来表示它,还可以通过点值表示法来表示它。

具体的,对于一个 \(n-1\) 次的多项式 \(f(x)\),我们可以通过 \(n\) 个互不相同的点唯一确定这个多项式,即 \(f(x)=[(x_0,y_0),(x_1,y_1),\cdots,(x_{n-1},y_{n-1})]\),其中 \(y_i=f(x_i)\)。

这种表示法对我们来说是很好的,因为对于两个多项式 \(f(x),g(x)\),只要把点值表示中 \(y_i\) 两两相乘即可求出 \(h(x)\) 的点值表达(注意:由于 \(h(x)\) 是 \(n+m-2\) 次多项式,所以需要 \(n+m-1\) 个点才能唯一确定 \(h(x)\))。这样,我们只需要 \(\mathcal{O}(n)\) 的时间复杂度即可求出两个多项式的卷积。

现在让我们整理一下算法流程:

  1. 输入两个多项式 \(f(x),g(x)\)
  2. 把 \(f(x),g(x)\) 各转成点值表达法(需要 \(n+m-1\) 个点)
  3. 把点值表达法的 \(y_i\) 两两相乘
  4. 把点值表达法转成 \(h(x)\) 的系数表达法
  5. 输出多项式 \(h(x)\)。

现在为止一切都很好,但是我们会发现还存在一个问题:系数表达法和点值表达法的转换依旧是 \(O(n^2)\) 的,所以这个算法除了增大常数之外没有任何好处。

你先别急,先看完下面再说

系数表达法 -> 点值表达法

让我们先从简单的多项式入手,比如 \(f(x)=x^2\),我们如何在较快的时间内求出它的点值表达呢?

很简单,我们可以代入一系列的正负值 \(\{\pm x_0,\pm x_1,\cdots,\pm x_{n/2-1}\}\),因为我们有 \(f(x)=f(-x)\),所以我们只需要求出 \(\dfrac{n}{2}\) 个点的点值表达了。

稍难一点,设 \(f(x)=x^3\),由于 \(f(-x)=-f(x)\),所以我们依旧可以参考上面的流程。

一般的,对于多项式 \(f(x)=\displaystyle\sum_{i=0}^{n-1}f_ix^i\),我们可以把它分为两半,即 \(f_0(x)=\displaystyle\sum_{i=0}^{n/2-1}f_{2i}x^i\) 和 \(f_1(x)=\displaystyle\sum_{i=0}^{n/2-1}f_{2i+1}x^i\),那么我们有

\[\begin{aligned} f(x_i)&=f_0(x_i^2)+x_if_1(x_i^2)\\ f(-x_i)&=f_0(x_i^2)-x_if_1(x_i^2)\\ \end{aligned} \]

可以发现对于 \(f_0\) 和 \(f_1\),这等价于原问题:多点求值,因此我们可以分治下去求解。

看上去非常不错,但事实上这种做法暗藏着一个陷阱:经过一轮迭代后 \(x_i^2\) 均为正值,因此不能再套用这种做法。

怎么解决呢?

可以发现我们想要的是一系列值 \(x_0,x_1,\cdots,x_{n-1}\),使得 \(x_{2i}=-x_{2i+1}\),\(x_{4i}^2=-x_{4i+2}^2\),\(x_{8i}^4=-x_{8i+4}^4\cdots\)

这时就需要引入复数了。

不妨设 \(x_0=1\),则显然 \(x_1=-1\),那么有 \(1=x_2^2\),由于 \(x_i\) 互不相等,故 \(x_2,x_3=i,-i\),以此类推。我们会发现 \(x_i\) 实际上就是 \(x^n=1\) 的全部复数解,或者说 \(\omega_n\)。

怎么计算 \(\omega_n\) 呢?可以发现单位根其实就是把单位圆进行了 \(n\) 等分,即 \(\omega_n^k=e^{i(2k\pi/n)}=\cos(2k\pi/n)+i\sin(2k\pi/n)\)。

至此,我们就可以通过分治将此算法做到 \(\mathcal{O}(n\log n)\) 了。

点值表达法 -> 系数表达法

考虑系数表示法变成点值表示法的另外一种方式:线性代数。

我们有

\[\begin{cases} f_0+x_0f_1+x_0^2f_2+\cdots+x_0^{n-1}f_{n-1}=f(x_0)\\ f_0+x_1f_1+x_1^2f_2+\cdots+x_1^{n-1}f_{n-1}=f(x_1)\\ \vdots\\ f_0+x_{n-1}f_1+x_{n-1}^2f_2+\cdots+x_{n-1}^{n-1}f_{n-1}=f(x_{n-1})\\ \end{cases} \]

使用矩阵表示即为

\[\begin{bmatrix} f(x_0)\\ f(x_1)\\ \vdots\\ f(x_{n-1}) \end{bmatrix} = \begin{bmatrix} 1 & x_0 & x_0^2 & x_0^3 & \cdots & x_0^{n-1}\\ 1 & x_1 & x_1^2 & x_1^3 & \cdots & x_1^{n-1}\\ 1 & x_2 & x_2^2 & x_2^3 & \cdots & x_2^{n-1}\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\ 1 & x_{n-1} & x_{n-1}^2 & x_{n-1}^3 & \cdots & x_{n-1}^{n-1}\\ \end{bmatrix} \begin{bmatrix} f_0\\ f_1\\ \vdots\\ f_{n-1} \end{bmatrix} \]

代入 \(x_k=\omega_n^k\) 可得

\[\begin{bmatrix} f(x_0)\\ f(x_1)\\ \vdots\\ f(x_{n-1}) \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 & 1 & \cdots & 1\\ 1 & \omega_n & \omega_n^2 & \omega_n^3 & \cdots & \omega_n^{n-1}\\ 1 & \omega_n^2 & \omega_n^4 & \omega_n^6 & \cdots & \omega_n^{2(n-1)}\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\ 1 & \omega_n^{n-1} & \omega_n^{2(n-1)} & \omega_n^{3(n-1)} & \cdots & \omega_n^{(n-1)(n-1)}\\ \end{bmatrix} \begin{bmatrix} f_0\\ f_1\\ \vdots\\ f_{n-1} \end{bmatrix} \]

那么如果我们想要进行 IFFT,本质上就是对上面这个大矩阵求逆。

由于这是一个有一些很好的性质的矩阵,所以它的逆也非常的漂亮:

\[\begin{bmatrix} 1 & 1 & 1 & 1 & \cdots & 1\\ 1 & \omega_n & \omega_n^2 & \omega_n^3 & \cdots & \omega_n^{n-1}\\ 1 & \omega_n^2 & \omega_n^4 & \omega_n^6 & \cdots & \omega_n^{2(n-1)}\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\ 1 & \omega_n^{n-1} & \omega_n^{2(n-1)} & \omega_n^{3(n-1)} & \cdots & \omega_n^{(n-1)(n-1)}\\ \end{bmatrix}^{-1}=\frac{1}{n}\begin{bmatrix} 1 & 1 & 1 & 1 & \cdots & 1\\ 1 & \omega_n^{-1} & \omega_n^{-2} & \omega_n^{-3} & \cdots & \omega_n^{-(n-1)}\\ 1 & \omega_n^{-2} & \omega_n^{-4} & \omega_n^{-6} & \cdots & \omega_n^{-2(n-1)}\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\ 1 & \omega_n^{-(n-1)} & \omega_n^{-2(n-1)} & \omega_n^{-3(n-1)} & \cdots & \omega_n^{-(n-1)(n-1)}\\ \end{bmatrix} \]

不难发现逆矩阵和原矩阵几乎一模一样,也可以使用 FFT 的方式计算。

至此,我们可以在 \(\mathcal{O}(n\log n)\) 的时间内计算两个多项式的乘积。

参考视频

标签:多项式,FFT,cdots,理解,深入,点值,bmatrix,omega,vdots
From: https://www.cnblogs.com/bykem/p/17124573.html

相关文章

  • 对于前几天进行的课堂测试的错误的更正的理解(期待指正)
    有关更正内容的描述本次更正内容,主要是对于用户权限管理的改正,也是我第一次做这样的题目类型具体实现第一项--实现权限列表所谓权限,也就是用户的菜单项,即登录之后,进入......
  • IaaS--云虚拟机(一)(何恺铎《深入浅出云计算》笔记整理)
    【概念讲解】云虚拟机的体系结构,就是全面解耦的计算存储分离的设计思想。传统的虚拟化,往往是对单一物理机器资源的纵向切割,计算、存储、网络等各方面的能力都是一台物理......
  • 字符串常用类及常量池和扩容机制理解
    字符串相关类:String、StringBuffer、StringBuilder  字符串相关的类:* 1.String字符串类,底层是基于常量char[],一旦创建长度就固定不变了,适用于字符串不经常增删改的......
  • 接口的理解
    1.1概述接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK7及以前),默认方法......
  • Kubernetes架构设计Concepts之理解Kubernetes API之理解Kubernetes对象
     首先我说下为什么去翻译这些文章,当然也有一些不少文章是翻译的,但是没有对照,或者是全中文的,这个时候你就不好判断理解,特别是比如:从代码角度和运维角度,代码角度呢,我们看到......
  • 简单理解类加载器的基本过程和作用
    类加载的大致过程编写.java文件,该文件存储的程序需要执行的逻辑内容,将.java经过Java编译器编译之后生成对应的.class后缀文件,class文件是.java文件经过转换之后的JVM虚拟......
  • 【图像处理基础】YUV格式理解
     1.YUV数据格式简介YUV,是一种颜色编码方法。常使用在各个视频处理组件中。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma)。NV......
  • 01 整体理解异常和错误
    整体理解异常和错误什么是异常异常结构体系分类ErrorException代码packagecom.zhan.base06Exception.demo01;publicclassTest01{//异常publics......
  • 【论文阅读】- 我对“AlexNet”的理解
    ......
  • PID参数的一些理解
    引入PID控制在工业生产的控制领域虽然年头已久,但是依然占有主要的地位,靠的就是它简洁优美的控制逻辑。虽然网上已经有很多相关的科普说明了,但是有了点新的想法,就在这里和......