首页 > 其他分享 >FFT

FFT

时间:2023-01-01 11:12:35浏览次数:50  
标签:frac int sum FFT +...+ omega

最近刚学了FFT,给我这个蒟蒻的脑细胞干废了,写篇笔记浅浅记录一下

先讲一下卷积,卷积就是求\(\int_{-\infty}^{\infty} f(\tau)g(x-\tau)d\tau\),这玩意的用处有一个是多项式乘法\(\sum_{i+j=x}^{n+m} a_ib_j(i\leq n,j\leq m)\),别的就比如说高斯模糊什么的

FFT,快速傅里叶变换,是一种快速求卷积的方式

点值表示法

正常我们表达都是\(f(x)=\sum_{i=0}^{n}a_ix^i\),就和两个点只能划一条线一样,一个\(n\)次多项式由\(n+1\)个点唯一确定,都说到这个了,那我挖个拉格朗日插值法的坑回头讲,就是\(f(x)=(x_0,f(x_0)),(x_1,f(x_1))...(x_n,f(x_n))\)

点值表示法意义下的乘法是\(O(n)\)
对于

\[f(x)=(x_0,f(x_0)),(x_1,f(x_1))...(x_n,f(x_n)) \]

\[g(x)=(x_0,g(x_0)),(x_1,g(x_1))...(x_n,g(x_n)) \]

乘积的点值表示法是

\[f\cdot g=(x_0,f(x_0)\cdot g(x_0)),(x_1,f(x_1)\cdot g(x_1))...(x_n,f(x_n)\cdot g(x_n)) \]

复数肯定都会,就不讲了

蝴蝶变换

在复数平面上的单位圆上的点都是可以取的,因为易见它们的若干次方可以为1,读者自证不难,这里不证

假设\(n=8\),我们取上面等分的8个点,逆时针从1开始,标到7

记编号为\(k\)的点代表的复数值为\(\omega_n^k\),易得\((\omega_n^1)^k=\omega_n^k\)

众所周知,\(e^{i\theta}=cos\theta+isin\theta\)并且\(e^{i\pi}+1=0\)

所以易见

\[\omega_n^k=cos\frac{k}{n}2\pi+isin\frac{k}{n}2\pi \]

还有几条

\[\omega_n^k=\omega_{2n}^{2k} \]

\[\omega_n^{k+\frac{n}{2}}=-\omega_n^k \]

\[\omega_n^0=\omega_n^n=1 \]

DFT是代入这些值的变换,是\(O(n^2)\)的

但DFT可以分治来搞,这就是FFT

FFT

\[A(x)=\sum_{i=0}^{n-1}a_ix^i=a_0+a_1x+...+a_{n-1}x^{n-1} \]

按\(A(x)\)下标的奇偶性把\(A(x)\)分成两半,右边再提一个\(x\)

\[A(x)=(a_0+a_2x^2+...+a_{n-2}x^{n-2})+(a_1x+a_3x^3+...+a_{n-1}x^{n-1}) \]

\[=(a_0+a_2x^2+...+a_{n-2}x^{n-2})+x(a_1+a_3x^2+...+a_{n-1}x^{n-2}) \]

两边非常相似,于是再设两个多项式\(A_1(x)\)和\(A_2(x)\),令

\[A_1(x)=a_0+a_2x+...+a_{n-2}x^{\frac{n}{2}-1} \]

\[A_2(x)=a_1+a_3x+...+a_{n-1}x^{\frac{n}{2}-1} \]

比较明显得出

\[A(x)=A_1(x^2)+xA_2(x^2) \]

设\(k \leq \frac{n}{2}\),把\(\omega_n^k\)带入,接下来几步变换要多想想单位根的性质

\[A(\omega_n^k)=A_1(\omega_n^{2k})+\omega_n^kA_2(\omega_n^{2k}) \]

\[=A_1(\omega_\frac{n}{2}^k)+\omega_n^kA_2(\omega_\frac{n}{2}^k) \]

对于\(A(\omega_n^{k+\frac{n}{2}})\)

\[A(\omega_n^{k+\frac{n}{2}})=A_1(\omega_n^{2k+n})+\omega_n^{k+\frac{n}{2}}A_2(\omega_n^{2k+n}) \]

\[=A_1(\omega_n^{2k})-\omega_n^kA_2(\omega_n^{2k}) \]

\[=A_1(\omega_\frac{n}{2}^k)-\omega_n^kA_2(\omega_\frac{n}{2}^k) \]

\(A(\omega_n^k)\)和\(A(\omega_n^{k+\frac{n}{2}})\)后面只有符号不同

就是只要已知\(A_1(\omega_\frac{n}{2}^k)\)和\(A_2(\omega_\frac{n}{2}^k)\),就可以求\(A(\omega_n^k)\)和\(A(\omega_n^{k+\frac{n}{2}})\)了

现在我们就可以递归分治来搞FFT了

每一次回溯时只扫当前前面一半的序列,即可得出后面一半序列的答案

\(n==1\)时就一个常数项,直接\(return\)

\(O(nlognloglogn)\)

优化-迭代版FFT

  • 每个位置分治后的最终位置为其二进制翻转后得到的位置

读者自证不难,这里不证

这样的话我们可以先把原序列变换好,把每个数放在最终的位置上,再一步一步向上合并

一句话就是可以\(O(n)\)预处理第\(i\)位最终的位置\(rev[i]\)

\(O(nlogn)\)

IFFT

  • 一个多项式在分治的过程中乘上单位根的共轭复数,分治完的每一项除以\(n\)即为原多项式的每一项系数

证明

设\((y_0,y_1,y_2,...,y_{n-1})\)为\(A(x)=a_0+a_1x+...+a_{n-1}x^{n-1}\)做完FFT的样子,设\(B(x)=y_0+y_1x+...+y_{n-1}x^{n-1}\),把它们的倒数\(\omega_n^0,\omega_n^{-1},...,\omega_n^{1-n}\)带入做FFT,得到\((z_0,z_1,...,z_{n-1})\)

\[z_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i \]

\[=\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j(\omega_n^i)^j)(\omega_n^{-k})^i \]

\[=\sum_{j=0}^{n-1}a_j(\sum_{i=0}^{n-1}(\omega_n^i)^{j-k}) \]

当\(j=k\)时,易知

\[\sum_{i=0}^{n-1}(\omega_n^i)^{j-k}=n \]

否则,通过等比数列求和可知

\[\sum_{i=0}^{n-1}(\omega_n^i)^{j-k}=\frac{(\omega_n^{j-k})^n-1}{\omega_n^{j-k}-1}=\frac{(\omega_n^n)^{j-k}-1}{\omega_n^{j-k}-1}=\frac{1-1}{\omega_n^{j-k}-1}=0 \]

所以

\[z_k=na_k \]

\[a_k=\frac{z_k}{n} \]

证毕Q.E.D

代码

学会了FFT之后就可以自己手搓板子啦

注意:FFT的\(n\)只能是\(2^k\)

void fft(cp *a,int inv){//a是要改变的数组,inv是区分FFT和IFFT的
        for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));//二进制翻转
	for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);//防止交换两次,就是没交换
	for(int mid=1;mid<n;mid<<=1){//二分长度
		cp temp(cos(pi/mid),inv*sin(pi/mid));//这个就是单位根,遍历每一个omega的
		for(int i=0;i<n;i+=(mid<<1)){
			cp omega(1,0);//初始元
			for(int j=0;j<mid;j++,omega*=temp){
				cp x=a[i+j],y=omega*a[i+j+mid];
				a[i+j]=x+y,a[i+j+mid]=x-y;//这个就是蝴蝶变换什么的
			}
		}
	}
	if(inv==-1) for(int i=0;i<n;i++) a[i]/=n;//IFFT
}

来实践一下

A*B Problem

两个整数\(a\)和\(b\)可以看做两个多项式\(x=10\)带入的结果,可以把每一位看成系数,\(a=a_na_{n-1}...a_1a_0\)可以看成多项式\(a=a_nx^n+a_{n-1}x^{n-1}...+a_1x+a_0\)当\(x=10\)时的数,看到乘法就是这两个多项式的乘积带回\(x=10\),所以,开卷!!!

正常的运算卷积速度,或者说乘法,是\(O(n^2)\)的,即使DFT和IDFT也是\(O(n^2)\)的

Karatsuba乘法的时间复杂度是\(O(n^{log3})≈O(n^{1.585})\)

关于NTT,实际上就是利用一些特殊的质数(例如\(998244353\),这些质数的特征都是可以可以表示 \(q\times 2^k + 1\)的形式)进行膜意义下的运算代替浮点复数运算来保证精度,原理是质数原根和复数单位根在DFT运算中具有相同的性质

这时候就可以用FFT啦!主要是因为我不会NTT,回头会了再发一篇,诶不对,我好像又挖了一个坑

在2019年之前,FFT的时间复杂度是\(O(nlognloglogn)\),虽然\(loglogn\)这项系数极小,但是直到2019年才去除掉,变成\(O(n logn)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define cp complex<double>
const double pi=acos(-1);
const int N=2097152+114514;
int len1,len2,n=1,k=0;
string a1,b1;
cp a[N],b[N]; 
int ans[N],rev[N];
void fft(cp *a,int inv){
	for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int mid=1;mid<n;mid<<=1){
		cp temp(cos(pi/mid),inv*sin(pi/mid));
		for(int i=0;i<n;i+=(mid<<1)){
			cp omega(1,0);
			for(int j=0;j<mid;j++,omega*=temp){
				cp x=a[i+j],y=omega*a[i+j+mid];
				a[i+j]=x+y,a[i+j+mid]=x-y;
			}
		}
	}
	if(inv==-1) for(int i=0;i<n;i++) a[i]/=n;
}
main(){
	cin>>a1>>b1;
	len1=a1.length(),len2=b1.length();
	for(int i=0;i<len1;i++) a[i]=(double)a1[len1-i-1]-48;
	for(int i=0;i<len2;i++) b[i]=(double)b1[len2-i-1]-48;
	while(n<len1+len2-1) n<<=1,k++;
	for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	fft(a,1),fft(b,1);
	for(int i=0;i<n;i++) a[i]*=b[i];
	fft(a,-1);
	for(int i=0;i<n;i++) ans[i]+=(int)(a[i].real()+0.5),ans[i+1]+=ans[i]/10,ans[i]%=10;
	while(!ans[n]&&n) n--;
	for(int i=n;i>=0;i--) cout<<ans[i];
	return 0;
}

标签:frac,int,sum,FFT,+...+,omega
From: https://www.cnblogs.com/liaiyang/p/17017847.html

相关文章

  • 手撕fft算法--fft原理和源码解析
    一前言 在音频信号处理中,fft变换是一个无法绕过过去的存在。借着一次算法出来的机会,把fft熟悉一下不为过啊。 二问题 这里,其实是由一个问题驱动的,那......
  • FFT入门——学习笔记
    FFT入门给一个非常好的入门视频:快速傅里叶变换复数与单位根定义:\(i^2=-1\)为虚数单位,我们称形如\(a+bi(a,b\inR)\)的数为复数。我们可以用复数在复平面上表示点\((0,......
  • 选带快速傅立叶变换ZOOM-FFT的matlab实现
    up目录一、理论基础二、核心程序三、测试结果一、理论基础zoom-fft是一个信号传输过程,其中输入信号被向下混频到基带,然后被抽取,然后被传递到标准FFT。ZOOM-FFT称为细......
  • Windows7下git配置difftool
    GIT是一个代码版本控制工具,是软件开发团队中必不可少的一类工具,类似的工具还有像SVN,CVS等;在此之前我一直使用的SVN,因为SVN在windows下有很好的客户端【小乌龟】,使用起来简单......
  • 利用FFT计算非平稳随机信号的WVD分布
    up目录一、理论基础二、核心程序三、测试结果一、理论基础fft:快速傅里叶变换(fastFouriertransform),即利用计算机计算离散傅里叶变换(DFT)的高效、快速计算方法......
  • matlab_fft函数c语言实现
    前言最近工作移植PPG算法,将MATLAB上代码移植到嵌入式设备上去。因为心率算法利用FFT实现会较为简单,所以又重新了解了一下大学里学的FFT,并写了C语言实现MATLAB的FFT接口的......
  • quartus中使用FFT IP核
     一、准备工作  首先需要把需要的器材准备好,我使用的是quartus18.0,并且要使用IP核被破解的版本,不然无法使用其中的FFT和NCO,一定要注意,quartus对于版本非常敏感,一定要......
  • error: #20: identifier "arm_cfft_instance_f64" is undefined
    在使用Keil5的过程中,偶尔遇到这个问题,以及类似的问题,报错的数量大概200多个。errortype>(42):error:#20:identifier"arm_cfft_instance_f64"isundefined其他的kei......
  • git difftool配置
    gitconfig--globaldiff.toolvimdiffgitconfig--globaldifftool.promptfalsegitconfig--globalalias.ddifftoolgitconfig--globaldifftool.trustExitCode......
  • 图像处理结束: 图像变化 ----- 傅里叶变换 FT / FFT
    (一)基础概念Fourier变换应用广泛:信号处理、图像处理。图像Fourier变换是将图像从图像空间变换到频率空间,从而可利用傅里叶频谱特性进行图像处理。Fourier变换是将一个函......