FTT从入门到自闭
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FTT从入门到自闭相关的知识,希望对你有一定的参考价值。
FTT从入门到自闭
0.基本定义
FFT全称(Fast Fourier Transformation)
中文名:快速傅里叶离散变换
作用是能以 O ( n l o g n ) O(nlogn) O(nlogn) 计算多项式乘法。
n n n次多项式:$F(x)=axn+bx{n-1}+cx^{n-2}\\dots $
F [ i ] F[i] F[i] 表示 F ( x ) F(x) F(x)的 i i i次项系数。
F ( x ) = ∑ i = 0 n F [ i ] x i F(x)=\\sum\\limits_{i=0}^n F[i]x^i F(x)=i=0∑nF[i]xi
乘法的本质(卷积)
两个 n n n次多项式 A ( x ) , B ( x ) A(x),B(x) A(x),B(x)相乘。
C = A ∗ B C=A*B C=A∗B
C [ k ] = ∑ i = 0 k A [ i ] B [ k − i ] = ∑ i + j = k A [ i ] B [ j ] C[k]=\\sum\\limits_{i=0}^k A[i]B[k-i]=\\sum\\limits_{i+j=k}A[i]B[j] C[k]=i=0∑kA[i]B[k−i]=i+j=k∑A[i]B[j]
因此乘法的本质就是加法卷积。
暴力卷积
P3803 【模板】多项式乘法(FFT)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m;
ll a[N],b[N],c[N];
void mul(ll *a,ll *b,ll *c){
for(int k=0;k<=n+m;k++)
for(int i=0;i<=k;i++)
c[k]+=a[i]*b[k-i];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lld",&a[i]);
for(int i=0;i<=m;i++) scanf("%lld",&b[i]);
mul(a,b,c);
for(int i=0;i<=n+m;i++) printf("%lld ",c[i]);printf("\\n");
return 0;
}
1.DFT&IDFT
用 n + 1 n+1 n+1个点值(有序数对) 可以唯一确定一个 n n n次多项式。
因此便有了 F F T FFT FFT的算法流程:
把系数表达转换为点值表达 ⇒ \\Rightarrow ⇒ 点值表达相乘 ⇒ \\Rightarrow ⇒ 点值表达转换为系数表达。
把系数表达转换为点值表达”的算法叫做DFT
把点值表达转换为系数表达”的算法叫做IDFT(DFT的逆运算)
- 从一个多项式的系数表达确定其点值表达的过程称为求值(毕竟求点值表达的过程就是取了 n 个 x 然后扔进了多项式求了 n 个值出来);
- 而求值运算的逆运算(也就是从一个多项式的点值表达确定其系数表达)被称为插值.
总结
在平面直角坐标系中,给你(n+1)个点就能确定一个n次多项式(函数)。
点值表示相乘(点乘)远快于暴力卷积。
2.单位根(复数)及其性质
关于复数
复数相乘时,模长相乘,幅角相加!
单位根
看起来很高大上,这是傅里叶快速变换的重要内容。
- n次单位根(n为正整数)是n次幂为1的复数。
换句话说,就是方程 x n = 1 x^n=1 xn=1的复数解。
单位根的性质
- ω n 0 = 1 \\omega_n^0=1 ωn0=1
- ω n k = ( ω n 1 ) k \\omega_n^k=(\\omega_n^1)^k ωnk=(ωn1)k
- ω n j ∗ ω n k = ω n j + k \\omega_n^j * \\omega_n^k=\\omega_n^{j+k} ωnj∗ωnk=ωnj+k
- ω 2 n 2 k = ω n k \\omega_{2n}^{2k}=\\omega_{n}^k ω2n2k=ωnk
- 若 n n n为偶数, ω n k + n / 2 = − ω n k \\omega_{n}^{k+n/2}=-\\omega_n^k ωnk+n/2=−ωnk
总结
复数把数轴扩展到了复平面上,复数可以对应复平面上一个点。
复数也有四则运算。复数相乘时,模长相乘,幅角相加。
n次单位根(n为正整数)是n次幂为1的复数。
把单位根画到单位圆上之后,就能整出一些性质。
3.DFT的加速版本
F ( w n k ) = F L ( w n / 2 k ) + w n k F R ( w n / 2 k ) \\large F(w_n^k)=FL(w_{n/2}^k)+w_n^kFR(w_{n/2}^k) F(wnk)=FL(wn/2k)+wnkFR(wn/2k)
F ( w n k + n / 2 ) = F L ( w n / 2 k ) − w n k F R ( w n / 2 k ) \\large F(w_n^{k+n/2})=FL(w_{n/2}^k)-w_n^kFR(w_{n/2}^k) F(wnk+n/2)=FL(wn/2k)−wnkFR(wn/2k)
4.DFT的代码实现
还有一个小细节。
上文有一句话:“保证n是2的整幂,不会出现分得不均匀的情况。”
实际应用中, n n n不一定是 2 2 2的正整数次幂。
我们可以补项,在最高次强行添加一些系数为0的项(类似于高精度补0)。不影响我们的计算结果,却占了位置。(具体见代码)
讲完了这些我们可以开始写 D F T DFT DFT了
1.复数结构体
#include <iostream>
using namespace std;
struct CP
{
CP (double xx=0,double yy=0){x=xx,y=yy;}
double x,y;
CP operator + (CP const &B) const
{return CP(x+B.x,y+B.y);}
CP operator - (CP const &B) const
{return CP(x-B.x,y-B.y);}
CP operator * (CP const &B) const
{return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
CP operator / (CP const &B) const {
double t=B.x*B.x+B.y*B.y;
return CP((x*B.x+y*B.y)/t,
(y以上是关于FTT从入门到自闭的主要内容,如果未能解决你的问题,请参考以下文章