FFT 与 NTT 学习笔记

【概念】

点值:给定多项式 f(x)=a0+a1x++an1xn1x1xm,求 f(x1),f(x2),,f(xm)。(m=n

求点值的算法一般是 O(n2) 的,但对于特殊的多项式,可以做到更快。


插值:给定 (x0,y0),(x1,y1),,(xn1,yn1),其中 x0xn1 互不相同。求一个不超过 n1 次的多项式 f(x),使得 f(xi)=yi。可以观察到 f(x) 是存在且唯一的。

求插值的算法一般是拉格朗日插值,令多项式 Pi(x)=j=0,jin1(xxj)j=0,jin1(xixj)

容易观察到 Pi(xi)=1,Pi(xj)=0(j 不等于 i)

f(x)=Pi(xi)yi。这证明了 f(x) 是存在的。


下证唯一性。若有两个多项式 f(x),g(x) 均满足要求。

考虑 r(x)=f(x)g(x),则 r(x)x0xn1 处值为 0

r(x)n1 次多项式,却有 n 个不相同的根,所以 r(x)=0,所以 f(x)=g(x)

【单位根的性质】

n 次单位根是所有满足 zn=1z 们。

考虑复数,记 n 次单位根εn。(即在复单位圆上从 0i+1 逆时针转 360°n 得到的复数)

因为单位根的定义,所以 εn0n1 都满足 zn=1,所以他们刚好构成 zn=1 的所有解。

还有一个性质:εnk=ei2πnk=cos2πkn+isin2πkn

复数的折半定理ε2n2i=εni

【FFT:快速傅里叶变换】

考虑这样一个问题:给定 f(x),g(x),求 h(x)=f(x)g(x)

多项式的乘积,其实就是系数的卷积。所以 FFT 解决的是卷积的问题。

n=degf(x)+degg(x)+1,显然 degh(x)<n

如果直接循环做,是 O(n2) 的;但是 FFT 能做到 O(nlogn)


为了确定 h(x),只需要 hn 个点 x0xn1 上的值即可。

基本思路是:① 求出 f(x0)f(xn1)。 ② 求出 g(x0)g(xn1)。 ③ h(xi)=f(xi)g(xi) ④ 插值还原 h

拉格朗日插值也是 O(n2) 的,所以我们要用更快的插值方式,后面会说到。

这里我们取 xi=εni

(当 xi 取单位根时,点值称作 DFT,插值称作 IDFT)

  1. f(x)εn0n1 的值。

    我们用分治的思路解决这个问题。但在这之前,我们假定 n 是二的幂。如果 n 不是二的幂,补若干个 0 系数使 n 是二的幂。

    f(x)=a0x0+a1x1++an1xn1

    定义 f(0)(x)=a0x0+a2x1+a4x2++an2x(n2)/2

    定义 f(1)(x)=a1x0+a3x1+a5x3++an1x(n2)/2

    观察到 f(x)=f(0)(x2)+xf(1)(x2)。问题转化为计算 f(0)(x)f(1)(x)εn20n21 的值(求平方项的值)。

    (折半定理:εn2=εn21,除以 2 后再对 n/2 取模)

    于是可以递归 f(0)(x),f(1)(x) 求解。则 f(x)εnk 的点值 y(εnk)=y(0)(εn2kmodn2)+εnky(1)(εn2kmodn2)。可以 O(n) 求得。

  2. g(x) 的点值。与上同理。

  3. 插值还原 h(x)。也同样采取递归的思路。

    这里引入矩阵的角度理解点值。(εn0=1

    [y0y1y2yiyn1]=[11111εn1εn2εnn11εn2εn4εn2(n1)1εniεn2iεni(n1)1εnn1εn2(n1)εn(n1)(n1)][a0a1an1]

    记等号左边列向量为 Y,矩阵为 Vn,右侧列向量为 A。观察到 Vn[i][j]=εnij(编号从 0 开始)。

    Y=VnA。插值就是已知 YA,即求 Vn1。那 Vn 的逆 Vn1 是什么呢?

    形式也很简单:Vn1[i][j]=1nεnij。编号也是从 0 开始。

    验证的话只要证明他俩相乘等于单位矩阵(对角线 1 其余 0)。

    观察到 VnVn1 就是把每一个 ε 都换成了他的共轭。类似点值的递归公式 y(εnk)=y(0)(εn2kmodn2)+εnky(1)(εn2kmodn2),可以写出插值的递归公式 a(εnk)=a(0)(εn2kmodn2)+εnka(1)(εn2kmodn2)

    递归的写法常数巨大。但这个过程其实可以用非递归实现。观察我们递归时哪些值被处理。

    (注意每次不是分首尾两半而是奇偶两半)

    发现了吗?最下面一层的二进制数如果从右往左读,刚好是 02n1

    还有一个优化:上面我们对 f,g 做了两次 DFT,对 h 做了一次 IDFT,共三次。但是如果 f,g 的系数都是实数,其实可以只做两次。

    摘自知乎。

至此,FFT 的算法介绍完毕。

【FFT 应用】

大整数乘积

一个 n 位的整数其实可以看作是 n1 次多项式的 x10 的结果。两个整数的乘积也可以看作是两个多项式的卷积。

合法平行四边形

题意:

考虑一个合法的平行四边形,将它补全成一个正方形。

则它的面积可以表示为 (a+c)(b+d)acbd=ad+bc=n。问题即求有多少个四元组 a,b,c,dZ+ad+bc=n

sii 的因数个数。枚举 adbca,d 的组数就是 sadb,c 的组数就是 sbc

所以 f(n)=i+j=nsisj,即 fs 的卷积。

把很像卷积的形式变成标准卷积:考虑拆开两半,一半标准形式,另一半对称地求。

给出 n 个数 q1,q2,qn,定义

Fj = i=1j1qi×qj(ij)2  i=j+1nqi×qj(ij)2

Ei = Fiqi

1in,求 Ei 的值。


可以理解 q1qnn 个排成一排的电荷。Fj 就是根据万有引力公式算出的其他电荷对 j 电荷的合力。

Ej=i<jqj(ij)2i>jqj(ij)2。怎么转化为卷积的形式?

定义

dk={1k2k>00k=01k2k<0

Ej=i<jqidji+i>jqidji=iqidji

但这不是标准卷积的形式,因为这里的 i 可以 >j,标准形式是不能 >j 的。这里有两种解法。


法一:考虑平移。

Ej+n=Ej,dk+n=dk,qi|i>n=0

Ej+n=i=1j+nqidji+n,是标准卷积形式。


法二:考虑对称。

Uj=i<jqidji,Vj=i>jqidij

Uj 是标准卷积形式,但是 Vji>j,不是标准形式。

考虑 V,q 进行翻转。Vi=Vn+1i,qi=qn+1i。则 Vj=i<jqidji,是标准形式。

Super Rooks on Chessboard

普通的车能控制所在行列;超级车除此之外,还能控制所在的主对角线(左上-右下的对角线)。

棋盘 rc 列,有 N 个超级车,超级车的位置预先给出。问:有多少个格子不被控制。

FFT + 容斥。

先反过来,求有多少个格子被至少一个控制到。

R 表示所有被同行的车控制的格子的集合,C 表示所有被同列的车控制的格子的集合,D 表示所有被主对角线的车控制的格子的集合。

即求 |RCD|=|R|+|C|+|D||RC||RD||CD|+|RCD|

难点在于求 |RCD|

r1n 行有车,c1m 列有车,d1l 对角线有车。(对角线标号:yx

※ 即求有多少 (i,j,k) 使得 cjri=dk

变形一下,ri+dk=cj。考虑两个多项式 r(x)=xri,d(x)=xdi

[xcj]r(x)d(x)=i,k[ri+dk=cj]

答案就是 ans=i[xci]r(x)d(x)

Triple Sums

给定 a1an,ai20000,n40000。对所有 S,问有多少三元组 (i,j,k) 满足 i<j<k,ai+aj+ak

如果不限制 i<j<k[xS](i=1nxai)(j=1nxaj)(k=1nxak) 是答案。

如何从上面转移到 i<j<kxaixajxak

(xai)3=i=1nx3ai+3ijx2aixaj+6i<j<kxaixajxak

要求的是 6i<j<kxaixajxak

等号左边用两次 FFT 可求。i=1nx3ai 也很简单。真正要求的是 3ijx2aixaj

考虑 (x2ai)(xai)=i=1nx3ai+ijx2aixaj。左侧可用一次 FFT,右侧第一项很简单,于是可以求出来。

(感觉很像 a2+b2+c2=(a+b+c)22(ab+bc+ca) 吧?)

Point Distance

(求有哪几种距离,和有几种点对是这个距离。n1024

下面从 0n1 编号。

实际上是求 Dx,y=i=0n1j=0n1ci,jci+x,j+y

暴力算是 O(n4) 的。必须优化。

ϕ(x,y)=x2n+y。从此我们用 ϕ 让一个数对应一个点对。从二维问题变成一维。这里为什么要定义成 ×2n 而不是 ×n?因为下面 ϕ(i,j)+ϕ(x,y) 会用到 n2n 的位置,我们需要将它定义成 0

Dϕ(x,y)=i=0n1j=0n1Cϕ(i,j)Cϕ(i,j)+ϕ(x,y)

ϕ(i,j)+ϕ(x,y)=(i+2n+x+2n+j+y)=ϕ(i+x,j+y)

Dz=uCuCu+z,这个式子就是转化为一维的形态,对应上面那个枚举二维的式子,uϕ(i,j)

(当 u 对应到 n1×n1 的矩阵时,Cu=Ci,j;否则 Cu=0。所以有很多位置都是 0

但是还不是卷积的形式。卷积是求和后等于左边,这里的做差后等于左边。

M=2n2,令 Ck=CM+1K,DK=DM+1K。(翻转)

DM+1z=uCuCM+1(u+z),这下变成标准形式卷积了。

我们有 C,可以翻转求出 C,用 FFT 把 C,C 卷起来得到 DD 翻转回去得到 D。一维的 D 转回二维后得到答案。

HDU4609

1a1a2an1e5n1e5

计数 i,j,kai+aj>ak,i<j<k

老样子,先忽略 i<j<k 怎么算?

对于固定的 k,答案 total=w=k+12e5[xw](ixai)(jxaj)。也就是多项式 (ixai)(jxaj) 的一个后缀和。

怎么转化?基本思路还是容斥。用 total 减去一些。

分类:

  1. i=j。即 2ai>ak。这一种的数量也是一个后缀和 S,可以求。

  2. i=kj。即 ai+aj>ai,这种情况肯定成立,于是这一种的 (i,j,k) 的数量就是数对 (i,j) 的数量 n×(n1)

  3. j=ki。类似,这种三元组数量为 n×(n1)

  4. i,j,k 各不相同。

    1. i<j<kj<i<k。若原题的答案记作 ans,这种的情况数是 2ans

    2. k 不是最大值。这种情况因为 a 是升序排列的,一定满足条件。情况数就是 4(n3)

因此 tot=S+2n×(n1)+2ans+4(n3).

Sum of Arrays

a,b 是长为 n随机生成的数列,将他们任意排列后,令 ci=ai+bi,目标是让 c 中众数的出现次数最大。n,ai,bi105

先做一个简单的转化。

a[i]ai 出现的次数,b[i]bi 出现的次数。cnt[k]=imin(a[i],b[ki])。目标是找最大的 cnt[k]

然后怎么转化成卷积的形式?这个 min 我们很不喜欢。

cnt[k]=i=0kx=1+[a[i]x][b[ki]x]=x=1+i=0k[a[i]x][b[ki]x]

i=0k[a[i]x][b[ki]x]=cntx[k]

假定 x 固定。u[i]=[a[i]x],v[i]=[b[i]x],则 cntx[k]=i=0ku[i]v[ki] 是标准形式卷积。

也就是对于固定的 x,我们可以 O(nlogn) 求出 cntx[k]。但是这里的 x 是枚举到 + 的。

注意到题目保证 a,b 随机生成,说明 a[i],b[i] 应该不会太大。

我们可以对于 x=120cntx[k] 用 FFT 求,复杂度 O(20nlogn);对于 x=21+ 的,可以直接暴力找出所有 >21a[i],b[i]。虽然复杂度玄学,但因为随机数据,跑得很快。

【NTT 算法】

NTT,数论变换。它解决的问题是:两个多项式 A,B 之积,结果每一项的系数都对质数 p 取模。

如果用 FFT 最后再取模有个问题:FFT 算出来的大多数都是浮点数,精度不足。

众所周知,FFT 是先在 n 次单位根的位置点值,然后再插值。(这里先把 A,B 扩充成 2 的幂)

阶:ab 的阶记作 δb(a)δb(a) 是所有满足 ax1(modb) 的正整数 x 中最小的那个。

原根:若 δb(a)=ϕ(b),称 a 是模 b 的原根。

posted @   FLY_lai  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示