FFT

news/2024/10/4 23:44:34

前置芝士:

欧拉说:$e^{i\theta}=cos(\theta)+isin(\theta)$

定义单位根$\omega_{n}{k}=e{n}}$

记$\theta=2\pi/n$ ,则$\omega_{n}{k}=e=cos(k\theta)+isin(k\theta)$

$1.\omega_{n}{0}=e=1,\omega_{n}{n}=e=cos(2\pi)+isin(2\pi)=1$

$2.(\omega_{n}{k})2=(e^{i 2\pi \frac{k}{n}})2=e{n/2}}=\omega_{n/2}^{k}$

$3.\omega_{n}{k}=cos(k\theta)+isin(k\theta)=-cos(k\theta+\pi)-isin(k\theta+\pi)=-e=-e^{i(\frac{2\pi k}{n}+\pi)}= -e{i2\pi(\frac{k+n/2}{n})}=-\omega_{n}$

$4.\omega_{n}{a}*\omega_{n}=e^{i2\pi \frac{a+b}{n}}=\omega_{n}^{a+b}$

基本思路

对于一个最高次为$n$次的多项式,我们可以通过$n+1$组$(x_i,F(x_i))$来唯一确定这个多项式的系数,这种表示方式叫做点值表示法。

$FFT$:把一个多项式从系数表示转化为点值表示法

$IFFT$:把一个多项式从点值表示法转为系数表示法

为求解$G(x)=H(x)F(x)$ 的系数,我们的思路是先把$H(x)$和$F(x)$通过$FFT$转为点值表示,分别为$(x_i,H(x_i))和(x_i,F(x_i))$,则$(x_i,H(x_i)F(x_i))$为$G(x)$的点值表示,再通过$IFFT$求出$G(x)$ 的系数

FFT

设$F(x)=a_0+a_1x+a_2x2...+a_{n-1}x$

我们可以利用单位根的一些性质进行快速计算,不妨将点值表示定为$(\omega_{n}{0},F(\omega_{n})),(\omega_{n}{1},F(\omega_{n})),...(\omega_{n}{n-1},F(\omega_{n}))$

问题转化为快速求解这$n$个点值

将$F(x)$的奇偶项分开(我们暂把$n$当作偶数)

$F(x)=(a_0+a_2x2+...+a_{n-2}x)+(a_1x+a_3x3+...+a_{n-1}x)$

$F(x)=(a_0+a_2x2+...+a_{n-2}x)+x(a_1+a_3x2+...+a_{n-1}x)$

记为$F(x)=A_0(x2)+xA_1(x2)$

代入 $x=\omega_{n}^{k}$

① $k<=n/2$

​ $F(\omega_{n}{k})=A_0(\omega_{n/2})+\omega_{n}{k}A_1(\omega_{n/2})$

②$k>n/2$

​ 记$k'=k-n/2$

​ $F(\omega_{n}{k})=A_0((-\omega_{n})2)+\omega_{n}A_1((-\omega_{n}{k'})2)$

​ $F(\omega_{n}{k})=A_0(\omega_{n/2})-\omega_{n}{k'}A_1(\omega_{n/2})$

那么对于$k<=n/2$

​ $F(\omega_{n}{k})=A_0(\omega_{n/2})+\omega_{n}{k}A_1(\omega_{n/2})$

​ $F(\omega_{n}{k+n/2})=A_0(\omega_{n/2})-\omega_{n}{k}A_1(\omega_{n/2})$

发现求出$k<=n/2$部分的所有$A_0(x)$和$A_1(x)$,则可同时得到$k>n/2$部分的$F(x)$

那么可以分治:

​ 我们最开始要求解$n$个长度为$n$的多项式$F(x)$

​ 而我们只要求$\frac{n}{2}$ 个长度为$\frac{n}{2}$ 的多项式$A_0(x)$和另$\frac{n}{2}$个长度为$\frac{n}{2}$的$A_1(x)$

​ ......以此类推

​ 最终只要求解共$n$ 个长度为 1 的多项式即可,长度为1意味着最高次是0,直接返回系数即可

则每一层的计算复杂度为O(n),共log(n)层,复杂度为O(nlogn)

为了避免一些不必要的讨论,且为了后续的一个优化,我们始终希望计算的每一个多项式的长度$n$是一个偶数,因此我们常把多项式的长度填充到$2^k$次为止,这也是前面直接把$n$当作偶数的原因。

IFFT

假设$G(x)=b_0+b_1x+b_2x2...+b_{n-1}x$

而我们现在已经求出了$G(x)$的点值表示$(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1})$其中$x_i=\omega_{n}^{i}$

类似于中国剩余定理,我们可以构造出一个多项式

令$\phi(x)=y_0+y_1x+y_2x2...+y_{n-1}x$

则$\phi(\omega_{n}{-i})=\displaystyle\sum_{j=0} y_j *(\omega_{n}{-i})j$

打开$y_i$有:$\phi(\omega_{n}{-i})=\displaystyle\sum_{j=0}\displaystyle\sum_{k=0}{n-1}b_k*(\omega_{n})k*(\omega_{n})j=\displaystyle\sum_{j=0}\displaystyle\sum_{k=0}{n-1}b_k*\omega_{n}=\displaystyle\sum_{k=0}{n-1}\displaystyle\sum_{j=0}b_k\omega_{n}{j(k-i)}=\displaystyle\sum_{k=0}(b_k\displaystyle\sum_{j=0}{n-1}\omega_{n})$

这个形式跟中国剩余定理的答案构造异曲同工,那我们进行分类

① $k=i$

​ $b_i\displaystyle\sum_{j=0}{n-1}\omega_{n}=b_in$

②$k\neq i$

​ $\displaystyle\sum_{j=0}{n-1}\omega_{n}=\displaystyle\sum_{j=0}{n-1}(\omega_{n})j=(\omega_{n})0+(\omega_{n})1...+(\omega_{n}){n-1}=\frac{(\omega_{n})n-1}{\omega_{n}-1}=\frac{(\omega_{n}{n})-1} {\omega_{n}^{k-i}-1} =\frac{1-1}{\omega_{n}^{k-i}-1}=0$

$\phi(\omega_{n}^{-i})=n*b_i$

$b_i=\frac{\phi(\omega_{n}^{-i})}{n}$

所以我们只需要对$\phi(x)$代入$\omega_{n}{-0},\omega_{n},...,\omega_{n}^{-(n-1)}$做一遍FFT,把他的每个点值除以$n$就得到了$G(x)$的系数

优化

直接进行分治需要预开大量空间,而且常数大,我们尝试不要递归分治,而从最底下往上进行多项式的计算

所以我们需要把多项式长度扩充到$2^k$整,并且因此最底层的序数十分有规律,我们模拟一次最底层系数的分配过程

$(a_0,a_1,a_2,a_3, a_4,a_5,a_6,a_7)$

$(a_0,a_2,a_4,a_6),(a_1,a_3,a_5,a_7)$

$(a_0,a_4),(a_2,a_6),(a_1,a_5),(a_3,a_7)$

$(a_0),(a_4),(a_2),(a_6),(a_1),(a_5),(a_3),(a_7)$

如果把最高层当作第一层,则第$i$层相当于按照第$i$位的二进制对序数进行分类,为$0$放左,为$1$放右

所以整个过程相当于从低位到高位进行了一次高位到低位的排序

其结果就是最底层的二进制是反过来的

正常序$(000),(001),(010),(011),(100),(101),(110),(111)$

最底层$(000),(100),(010),(110),(001),(101),(011),(111)$

记$rev(i)$为$i$的逆序

显然有递推关系 $i=rev(rev(i/2))*2+i&1$

则有$rev(i)=rev(rev(rev(i/2))*2+i&1)$

注意到$rev(rev(i/2))*2$是一个偶数,则最低位是空出来的,与$i&1$不产生进位,所以我们可以直接拆开括号,有

$rev(i)=rev(rev(rev(i/2))*2)+rev(i&1)$

由于rev本身代表了逆序操作,打开$rev$时要把位数倒过来,如下

$rev(i)=rev(rev(rev(i/2)))/2+(i&1)*2^{k-1}$

于是有$rev(i)=rev(i/2)/2+(i&1)*2^{k-1}$

#include<bits/stdc++.h>
using namespace std;const int N = 10000000 + 5;
const double Pi = acos(-1.0); inline int read(){char ch=getchar();int x=0;while(ch<'0'||ch>'9') ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x;
}complex <double> a[N],b[N];
int n,m,lim,l,rev[N];void FFT(complex<double> *A,int opt){for(int i=0;i<lim;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);for(int m=2;m<=lim;m<<=1){int mid=m>>1;complex<double> Wn(cos(2.0*Pi/m),opt*sin(2.0*Pi/m));for(int i=0;i<lim;i+=m){complex <double> w(1,0);for(int k=0;k<mid;++k,w=w*Wn){complex <double> x=A[i+k],y=w*A[i+k+mid];A[i+k]=x+y;A[i+k+mid]=x-y;}}}
}int main(){n=read(),m=read();for(int i=0;i<=n;++i) a[i].real(read());for(int i=0;i<=m;++i) b[i].real(read());lim=1,l=0;while(lim<n+m+1) lim<<=1,++l;for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)+((i&1)<<(l-1));FFT(a,1);FFT(b,1);for(int i=0;i<lim;++i) a[i]=a[i]*b[i];FFT(a,-1);for(int i=0;i<=n+m;++i)printf("%d ",int(a[i].real()/lim+0.5));return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/67824.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

【2024.10.4 闲话】0/99+

当符卡收取100次后,收率显示会从xx/99+变为master,收率master是成为神触第一步呢(笑)。今日推歌:没有。明天可能有。 今日 set:也没有。话说应该没人知道 set 是什么吧,总之不是 std::set。[ARC176E] Max Vector 给你两个长度为 \(N\) 的正整数序列: \(X=(X_1,X_2,\dot…

2024 ciscn WP

一、MISC1.火锅链观光打卡打开后连接自己的钱包,然后点击开始游戏,答题八次后点击获取NFT,得到有flag的图片没什么多说的,知识问答题兑换 NFTFlag{y0u_ar3_hotpot_K1ng}2.Power Trajectory Diagram方法1:使用py中的numpy和pandas库读取npz文件并保存为csv文件,代码如下:…

30. 协程

1.协程的概念 1.1 定义 进程是操作系统内部运行的程序 线程是进程内部运行的程序 协程是线程内部运行的程序 协程是单线程下的并发,又成微线程,英文名coroutine 1.2 协程的优点协程切换的开销更小 GIL锁导致同一时刻只能运行一个线程,一个线程内不会限制协程数,单线程就可以…

.net core 安装服务

https://www.jianshu.com/p/e1b3b61f876a使用NSSM 后面的代码演示以Asp.net Core 2.1作为演示,其他.Net Core方式一致。 1、确保.Net Core程序可以正常运行 先把Asp.net Core发布,然后直接运行dotnet命令,确保程序可以运行并访问 2、使用NSSM安装dotnet 下载NSSM,使用命…

vs2015安装包丢失或损坏解决工具 或者不能启动

打开“本地组策略编辑器”(gpedit.msc)。展开“计算机配置”>“管理模板”>“系统”>“Internet 通信管理”,然后选择“Internet 通信设置”。选择“关闭自动根证书更新”>,“禁用”,然后选择“确定”或“应用”。  下载最新的组件版本(备份的) https://lea…

uboot 启动自编写程序的方式

uboot 启动自编写程序的方式 uboot 存在 boot 命令。 自己最初在尝试撰写串口程序时,选择了使用汇编来完成。 在这段时间,自己使用 go 命令来尝试载入程序 先是在 Ubuntu 上搭建 tftp 目录 # /etc/default/tftpd-hpaTFTP_USERNAME="tftp" TFTP_DIRECTORY="/ho…

20240924

[牛半仙的妹子 Tree(tree)](http://ac.robo-maker.cn/d/contest/p/ZY1044?tid=66f28cd11bca2159e88c8fb0) 我们会发现其实牛半仙发癫时就等于将以前的标记清空,从头开始,所以我们可以考虑根号分治,如果两个牛半仙发癫的时间间隔小于 \(\sqrt n\) ,那么我们可以直接暴力枚举两…