板子:FFT/NTT

本文介绍了FFT(快速傅里叶变换)和NTT(快速数论变换)的基本原理及其应用。FFT是用于在O(nlogn)时间内计算多项式乘法和卷积的技术,而NTT主要应用于有模计算,避免精度误差。文中详细阐述了FFT的优化原理、实现方式,并给出了相关代码示例,包括递归和非递归的实现。同时,还提及了NTT与FFT的相似性和在模运算中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

序言

这篇文章是自己看的,外面有这么多优秀的博客多好。
这是我在复赛前那个暑假的一个噩梦。。。最终我还是花了一天时间搞出来了,这说明我天赋一般,虽然即使说明了我天赋一般也没有什么用。。该干啥还是得干啥。其实天赋没啥用的,真正的是方法什么的比较重要,以及平时各种能力的积累,最终体现在天赋上,也就是说天赋是可以改变的,天赋和智商差距应该还是蛮大的。我在说啥。。。。。
感谢很多优秀的博主带来的优秀的文章,感谢同机房的yyn带了了符合我们机房风格的代码板子。。。。

FFT/NTT是干嘛的?

FFT中文叫做快速傅里叶变换。

就是在O(nlogn)的时间里面求出多项式乘法,如果硬乘的话是O(n^2).

当然还有一个作用就是求卷积,在O(nlogn)求出f(1)~f(n),其中f(i)=sum{g(j)*g(k-j),0<=j<=i},不会卷积没有关系的,大概看一下定义就行了,多项式乘法的实质其实也算是个卷积吧。

至于做题的话一个是优化多项式乘法,一个是求卷积形式的数学题,一般来说就是推公式推出卷积形式就套模板吧,

此外,这个真的就是个板子,因此即使有些细节无法理解,只要背下来问题也就不大了。

NTT中文叫做快速数论变换。

NTT基本上和FFT差不多,代码结构基本上完全一样,NTT对于有mod的,会比较快而且没有精度误差,因为它是建立在整数上的情况而不是FFT建立在复数的情况。

FFT原理

注:我们这里按照多项式乘法来理解

优化原理

我们是用什么方法是的O(n^2)的算法变成了O(nlogn)的呢?

首先我们需要知道,多项式有两种表示方法,系数表示法和点值表示法,系数表示法就是直接记录系数,点值表示法就是带入n个不同值的点,得到答案,也就是n项的多项式用n个(x,y)表示(n个方程解n个系数嘛)。

所以我们有一种叫做DFT的变换可以使得系数表达式通过O(nlogn)的时间变成点值表达式,然后点值表达式O(n)就能做完乘法,又可以用几乎相同的方法在O(nlogn)的时间里面变成系数表达式,最终就完成了时间复杂度O(nlogn)的做法。

当然这只是个意思,实际上肯定还是有很多数学技巧的。

实现原理

wn的递推是第一个用三角形式,后面的用指数形式。

A(x)=a0+a1∗x+a2∗x2+a3∗x3+a4∗x4+a5∗x5+⋯+an−2∗xn−2+an−1∗xn−1 $

A1(x)=a0+a2∗x+a4∗x2+⋯+an−2∗xn/2−1

A2(x)=a1∗x+a3∗x+a5∗x2+⋯+an−1∗xn/2−1

A(x)=A1(x^2)+x*A2(x ^2)

大概就是这个意思,加上单位复数根的一些性质,加上蝴蝶变换模仿递归操作,记住代码就好了。

其中x^2经过递归刚刚符合,wn(k,n)=-wn(k+n/2,n),所以一个加一个减

最后逆变换是用了一个矩阵思想,反矩阵,主要答案要/n;

一些操作

分治FFT

FFT可以完全看作乘法运算(比如它用来优化高精度乘法,而高精度乘法本身就是卷积运算),只不过是对于一个数组(多项式)进行操作。

所以有时候遇到快速幂的形式是可以用快速幂来做的。

循环卷积

有时候要把f[n]的值算作f[n%m]的值,那么累加上去即可,比如bzoj4827 [Hnoi2017]礼物

具体来说就是比如正常的是a1 * bn + a2 * bn-1+..

然后你要a1* b1+ a2 * bn +a3 * bn-1+…

序列反转

a1 *b1+ a2 *b2+…

把b序列反转了就变成了卷积形式

代码

递归处理应该比较好理解,然后看非递归,我们通过观察发现在递归处理一些元素之后原来的第i个元素,新下标恰好是i在二进制下的反转,因此我们通过实现预处理初最终位置来进行非递归,这一部也叫做蝴蝶变换,因为叉过来叉过去的很像蝴蝶嘛。

FFT

BZOJ2179 FFT(A*B)

#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=150000+105;
const double pi=acos(-1);
struct Complex
{
    double x,y;
    Complex(){};
    Complex(double _x,double _y){x=_x,y=_y;};//注意这里的几个赋值操作
    friend Complex operator+(Complex a,Complex b)
    {
        return Complex(a.x+b.x,a.y+b.y);
    }
    friend Complex operator-(Complex a,Complex b)
    {
        return Complex(a.x-b.x,a.y-b.y);
    }
    friend Complex operator*(Complex a,Complex b)//(a+bi)*(c+di)=ac+bd*i^2+(ad+bc)i=(ac-bd,(ab+bc)i)
    {
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值