FFT简明指南

简介

FFT是用来计算多项式卷积的东西。

多项式卷积: \(C=A\ast B\) ,即 \(c_k=\sum_{i+j=k} a_i\times b_j\) 。(假设下标范围 \(0-n\) )

直接按照定义做是 \(O(n^2)\) 的,但是FFT可以做到 \(nlog(n)\) 。

一些奇奇怪怪的东西(定义)

考虑一个 \(n\) 次多项式,取 \(n\) 个不同的 \(x\) 代入会得到 \(n\) 个 \(y\) ,然后发现由于我们可以解方程,因此只要不是无解、多解,我们可以说,一个 \(n\) 次多项式和一个(有 \(n\) 个点的集合一一对应)。

对于多项式 \(A=\sum_{i=0}^{n-1}a_i\times x^i\) ,定义其在 \(x=x_0\) 处的点值为 \(A(x_0)=\sum_{i=0}^{n-1}a_i\times x_0^i\) 。

多项式系数表示:对于 \(A=\sum_{i=0}^{n-1} a_i\times x^i\) ,系数表示为 \(A=\{a_0,a_1,...,a_{n-1}\}\) 。

多项式点值表示: \(A=\{(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1})\}\) 。如果 \(x\) 的取值确定的情况下,显然也可以写成 \(A=\{y_0,y_1,...,y_{n-1}\}\) 。这样的话,多项式的系数、点值表示都可以写成一个向量vector的形式了。

点值表示中,若A与B \(x\) 的取值相同,它们相乘只需要把 \(x\) 对应的 \(y\) 分别相乘即可。

n次单位根(其实n次单位根只应该有1个,但是为了方便这里就当作有n个):方程 \(x^n=1\) 的n个复数根,分别称为 \(w_n^0,w_n^1,w_n^2,...,w_n^{n-1}\) ,其中 \(w_n^0=1\) 。简记为 \(w_{n,0}\) 。不引起歧义的情况下,我会写成 \(w_0\) 。同时,在这里,n应该是2的幂次(原因后文会讲)。

主要用到的n次单位根的性质: \(w_{n,1}^k=w_{n,k},w_{n,k}=w_{2\times n,2\times k},w_{n,k+\frac{n}{2}}=-w_{n,k}\) 。

DFT,就是把一个长度为 \(n\) 次的向量(多项式系数表示)代入 \(n\) 次单位根,从而转换为另一个向量(多项式点值表示)。而IDFT,是把以上点值表示转换为系数表示。它们的复杂度都是 \(O(n\log n)\) 。

做法

数学

好,假设我们现在需要把一个长为 \(2^{len}=n\) 的多项式 \(A=\sum_{i=0}^{n-1}a_i\times x^i\) 转换为点值表示(系数表示和系数表示向量被认为是相同的)。

我们先把它分成两部分:

\[
A=\sum_{i=0}^{\frac{n}{2}-1}a_{2\times i}\times x^{2\times i}+\sum_{i=0}^{\frac{n}{2}-1}a_{2\times i+1}\times x^{2\times x+1}。
\]

构造两个新的多项式,其系数表示的向量分别为:

\[
A_1=\{a_0,a_2,...,a_{n-2}\}\A_2=\{a_1,a_3,...,a_{n-1}\}
\]

把这两个多项式分别变成点值表示(相当于递归向下做):

\[
A_1=\{(w_{\frac{n}{2},0},a_0),(w_{\frac{n}{2},1},a_2),...,(w_{\frac{n}{2},\frac{n}{2}-1},a_{n-2})\}\A_2=\{(w_{\frac{n}{2},0},a_1),(w_{\frac{n}{2},1},a_2),...,(w_{\frac{n}{2},\frac{n}{2}-1},a_{n-2})\}
\]

然后考虑我们需要的是A的点值表示。枚举每一个 \(n\) 阶单位根,我们需要知道A代入 \(w_n^i\) 的值。然后再看一下第一个公式

\[
A=\sum_{i=0}^{\frac{n}{2}-1}a_{2\times i}\times x^{2\times i}+\sum_{i=0}^{\frac{n}{2}-1}a_{2\times i+1}\times x^{2\times x+1}。
\]

又因为有 \((w_n^1)^2=w_n^2=w_{\frac{n}{2}}^1\) ,所以代入 \(w_n^i\) 后,点值就是A1在 \(w_{\frac{n}{2}}^i\) 处的点值加上 \(w_n^i\times A_2[w_{\frac{n}{2}}^i]\) 。

然后是IDFT。有一个结论,是说只要把以上的 \(n\) 次单位根全都变成相反数就行。证明自行百度。

代码

inline void FFT(Complex *A,int len,int fl);

DFT中,A开始存系数,后来存\(A[w_i]\)。

基本代码:

inline void FFT(Complex *A,int len,int fl){
    if (len == 1) return;
    Complex *A1, *A2;
    A1 = new Complex[len / 2], A2 = new Complex[len / 2];
    rep(i, 0, len / 2){
        A1[i] = A[i * 2];
        A2[i] = A[i * 2 + 1];
    }
    FFT(A1, len / 2, fl);
    FFT(A2, len / 2, fl);
    Complex w = init(len, fl), cur = (Complex)1;
    rep(i, 0, len){
        A[i] = A1[i % (len / 2)] + cur * A2[i % (len / 2)];
        cur = cur * w;
    }
}

如果把最终的数字转换的结果放出来,就是这样的:

int tmp;
inline void FFT(Complex *A,int len,int fl){
    // reverse
    int j = 0;
    rep(i, 0, len){
        if (j > i) Swap(A[i], A[j]);
        // reverse add j
    }
    // reverse
    Complex w, step, tmp;
    for (i = 1; i < len; i <<= 1){
        w = ...;
        for (int j = 0; j < len; j += i * 2){
            step = (Complex)1;
            for (k = j; k < j + i; ++k){
                tmp = step * A[j + i + k];
                A[j + i + k] = A[j + k] - tmp;
                A[j + k] += tmp;
                step *= w;
            }
        }
    }
}

(上面的加减可能需要理解)

NTT

如上,但是把所有的 \(n\) 次单位复根换成 \(\pm 3^{998244352/n}\mod{998244353}\) 。

原文地址:https://www.cnblogs.com/pupuvovovovo/p/11704961.html

时间: 2024-10-18 14:06:38

FFT简明指南的相关文章

git - 简明指南

助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics如有纰漏,请在 github 提报问题 安装 下载 git OSX 版 下载 git Windows 版 下载 git Linux 版 创建新仓库 创建新文件夹,打开,然后执行 git init以创建新的 git 仓库. 检出仓库 执行如下命令以创建一个本地仓库的克隆版本:git clone /path/to/repository 如果是远端服务器上的仓库,你的命令会是这个样子

Teamcity+SVN+VisualStudio之持续集成简明指南

Teamcity+SVN+VisualStudio之持续集成 简明指南 一.写在最前: 1.      各组件版本如下: Teamcity(简称tc)版本:8.1.4 SVN版本:TortoiseSVN 1.8.7 VS版本:VS2010. 10.0.30319.1 2.      关于CI(Continuousintegration,持续集成): 其重要性与关键性自不必多说,由于不是本文重点,因此略过.可以参考:http://en.wikipedia.org/wiki/Continuous_i

【转】git - 简明指南

git - 简明指南 助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics其他语言 english, deutsch, español, français, indonesian, italiano, nederlands, polski, português, русский, türkçe, ??????, 日本語, ??? Vietnamese 如有纰漏,请在 github 提报问题 安装 下载 git OSX 版 下载

AndroidAnnotations简明指南

AndroidAnnotations简明指南 Android 开源 AndroidAnnotations简介 AndroidAnnotations是注释框架,它能够让你快速进行Android开发的开源框架,它能让你专注于真正重要的地方. 使代码更加精简,使项目更加容易维护,它的目标就是"Fast Android Development.Easy maintainance".

[转载][献给想深入学习Linux开发的网友] Linux 应用程序开发到内核开发的简明指南

[献给想深入学习Linux开发的网友] Linux 应用程序开发到内核开发的简明指南. 前言,Linux/Unix 社区的版块不热闹,新手太多,高手太忙,偶把自己这几年的Linux学习的过程讲讲,希望能给大家有点帮助, 引导初学者学习的方向,少走弯路,事半功倍,同时也是抛砖引玉,请过来人热烈发言. 1. 首先熟悉 Linux  建议在 Windows 上安装 VMware Workstation 6.5.0(以下简称VM), 下载种子在 http://www.verycd.com上找. 安装好 

RESTful 接口实现简明指南

REST 简介 REST 是一个术语的缩写,REpresentational State Transfer,中文直译「表征状态转移」,这是个很拗口的词.我的建议是先不要强行理解,直接看怎么做,等对实施细节有一些了解后,再来看名字会有更深刻的理解.REST 是一套风格约定,RESTful 是它的形容词形式:比如一套实现了 REST 风格的接口,可以称之为 RESTful 接口. REST 对请求的约定 REST 用来规范应用如何在 HTTP 层与 API 提供方进行数据交互:在现阶段,你应该已经很

git - 简明指南(转)

安装 下载 git OSX 版 下载 git Windows 版 下载 git Linux 版 创建新仓库 创建新文件夹,打开,然后执行  git init 以创建新的 git 仓库. 检出仓库 执行如下命令以创建一个本地仓库的克隆版本: git clone /path/to/repository  如果是远端服务器上的仓库,你的命令会是这个样子: git clone [email protected]:/path/to/repository 工作流 你的本地仓库由 git 维护的三棵“树”组成

【转载】git - 简明指南

转载自http://rogerdudler.github.io/git-guide/index.zh.html 安装 下载 git OSX 版 下载 git Windows 版 下载 git Linux 版 创建新仓库 创建新文件夹,打开,然后执行 git init以创建新的 git 仓库. 检出仓库 执行如下命令以创建一个本地仓库的克隆版本:git clone /path/to/repository 如果是远端服务器上的仓库,你的命令会是这个样子:git clone [email protec

golang 项目实战简明指南

原文地址 开发环境搭建 golang 的开发环境搭建比较简单,由于是编译型语言,写好 golang 源码后,只需要执行 go build 就能将源码编译成对应平台(本文中默认为 linux)上的可执行程序.本文不再赘述如何搭建 golang 开发环境,只说明下需要注意的地方. 从官网下载对应平台的 golang 安装包中包括 golang 的编译器.一些工具程序和标准库源码.早期的 golang 版本中,需要设置 GOROOT 和 GOPATH 两个环境变量. 从 1.8 版开始,GOPATH