前言

昨天学了一晚上,终于搞懂了FFT。希望能写一篇清楚易懂的题解分享给大家,也进一步加深自己的理解。
FFT算是数论中比较重要的东西,听起来就很高深的亚子。但其实学会了(哪怕并不能完全理解),会实现代码,并知道怎么灵活运用 (背板子) 就行。接下来进入正题。

定义

FFT(Fast Fourier Transformation),中文名快速傅里叶变换,是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。
而在信奥中,一般用来加速多项式乘法
朴素高精度乘法的时间为O(n2)O(n^2)O(n2),但FFT能将时间复杂度降到 O(nlog2n)O(nlog_2n)O(nlog2​n)
学习FFT之前,需要了解一些有关复数和多项式的知识。

有关知识

多项式的两种表示方法

系数表示法

F[x]=y=a0x0+a1x1+a2x2+......anxnF[x]=y=a_0x^0+a_1x^1+a_2x^2+......a_nx^nF[x]=y=a0​x0+a1​x1+a2​x2+......an​xn
{a0,a1,a2,...,ana_0,a_1,a_2,...,a_na0​,a1​,a2​,...,an​} 是这个多项式每一项的系数,所以这是多项式的系数表示法

点值表示法

在函数图像中,F[x]F[x]F[x]这个多项式可以被n个点唯一确定,即代入n个点作为xxx,分别解出对应的 yyy,得到n个式子。把这n条式子联立起来成为一个有n条方程的n元方程组,每一项的系数都可以解出来.(可类比二元一次方程)
也就是说,使用{(x0,f[x0])(x_0,f[x_0])(x0​,f[x0​]),(x1,f[x1])(x_1,f[x_1])(x1​,f[x1​]),…,(xn,f[xn])(x_n,f[x_n])(xn​,f[xn​])}就可以完整描述出这个多项式,这就是 多项式的点值表示法

多项式相乘

设两个多项式分别为f(x)f(x)f(x),g(x)g(x)g(x),我们要把这两个多项式相乘 (即求卷积)。
如果用系数表示法:
我们要枚举fff的每一位的系数与ggg的每一位的系数相乘,多项式乘法时间复杂度O(n2)O(n^2)O(n2),这也是我们所熟知的高精度乘法的原理。
如果用点值表示法:
f[x]f[x]f[x]={(x0,f[x0])(x_0,f[x_0])(x0​,f[x0​]),(x1,f[x1])(x_1,f[x_1])(x1​,f[x1​]),…,(xn,f[xn])(x_n,f[x_n])(xn​,f[xn​])}
g[x]g[x]g[x]={(x0,g[x0])(x_0,g[x_0])(x0​,g[x0​]),(x1,g[x1])(x_1,g[x_1])(x1​,g[x1​]),…,(xn,g[xn])(x_n,g[x_n])(xn​,g[xn​])}
f[x]∗g[x]f[x]*g[x]f[x]∗g[x]={(x0,f[x0]∗g[x0])(x_0,f[x_0]*g[x_0])(x0​,f[x0​]∗g[x0​]),(x1,f[x1]∗g[x1])(x_1,f[x_1]*g[x_1])(x1​,f[x1​]∗g[x1​]),…,(xn,f[xn]∗g[xn])(x_n,f[x_n]*g[x_n])(xn​,f[xn​]∗g[xn​])}
我们可以发现,如果两个多项式取相同的xxx,得到不同的yyy值,那么只需要yyy值对应相乘就可以了!
复杂度只有枚举xxx的O(n)O(n)O(n)
那么问题转换为将多项式系数表示法转化成点值表示法。
朴素系数转点值的算法叫DFT(离散傅里叶变换),优化后为FFT(快速傅里叶变换)点值转系数的算法叫IDFT(离散傅里叶逆变换),优化后为IFFT(快速傅里叶逆变换)。之后我会分别介绍。

卷积

其实不理解卷积也没关系,但这里顺便提一下,可以跳过的
卷积与傅里叶变换有着密切的关系。利用一点性质,即两函数的傅里叶变换的乘积等于它们卷积后的傅里叶变换,能使傅里叶分析中许多问题的处理得到简化。

F(g(x)∗f(x))=F(g(x))F(f(x))F(g(x)*f(x)) = F(g(x))F(f(x))F(g(x)∗f(x))=F(g(x))F(f(x))

其中F表示的是傅里叶变换

复数

高中数学会详细讲解,知道的可以跳过这一部分,没学过也没关系,看以下内容应该能很清楚的理解。

1.定义

数集拓展到实数范围内,仍有些运算无法进行。比如判别式小于0的一元二次方程仍无解,因此将数集再次扩充,达到复数范围。
复数zzz被定义为二元有序实数对(a,b)(a,b)(a,b),记为z=a+biz=a+biz=a+bi,这里aaa和bbb是实数,规定iii是虚数单位。 (i2=−1i^2=-1i2=−1 即i=−1i=\sqrt{-1}i=−1​)
对于复数z=a+biz=a+biz=a+bi。实数aaa称为复数z的实部(real part),记作rez=arez=arez=a.实数bbb称为复数z的虚部(imaginary part)记作 Imz=b.
当虚部等于零时,这个复数可以视为实数
即当b=0b=0b=0时,z=az=az=a,这时复数成为实数;当且仅当a=b=0a=b=0a=b=0时,它是实数0;
当z的虚部不等于零时,实部等于零时,常称z为纯虚数
即当a=0a=0a=0且b≠0b≠0b​=0时,z=biz=biz=bi,我们就将其称为纯虚数。
将复数的实部与虚部的平方和的正的平方根的值称为该复数的模,记作∣z∣∣z∣∣z∣。
即对于复数z=a+bi,它的模为∣z∣=(a2+b2)∣z∣=\sqrt{(a^2+b^2)}∣z∣=(a2+b2)​

2.复数的几何意义

直接两张图搞定√ (应该可以一目了然)

3.运算法则

加法法则:(a+bi)+(c+di)=(a+c)+(b+d)i;(a+bi)+(c+di)=(a+c)+(b+d)i;(a+bi)+(c+di)=(a+c)+(b+d)i;
减法法则:(a+bi)−(c+di)=(a−c)+(b−d)i;(a+bi)-(c+di)=(a-c)+(b-d)i;(a+bi)−(c+di)=(a−c)+(b−d)i;
注:复数加减满足平行四边形法则


乘法法则:(a+bi)⋅(c+di)=(ac−bd)+(bc+ad)i(a+bi)·(c+di)=(ac-bd)+(bc+ad)i(a+bi)⋅(c+di)=(ac−bd)+(bc+ad)i
复数相乘一个重要法则:模长相乘,幅角相加。(这个定理很重要)
模长:这个向量的模长,即这个点到原点的距离。(不懂的可再看下向量的几何意义)。
幅角: 从原点出发、指向x轴正半轴的射线绕原点逆时针旋转至过这个点所经过的角。
在极坐标(可看成平面直角坐标系)下,复数可用模长r与幅角θ表示为(r,θ)(r,θ)(r,θ)。对于复数a+bia+bia+bi,r=(a²+b²)r=\sqrt{(a²+b²)}r=(a²+b²)​,θ=arctan(b/a)θ=arctan(b/a)θ=arctan(b/a)。此时,复数相乘表现为模长相乘,幅角相加。
除法法则:(a+bi)÷(c+di)=[(ac+bd)/(c²+d²)]+[(bc−ad)/(c²+d²)]i(a+bi)÷(c+di)=[(ac+bd)/(c²+d²)]+[(bc-ad)/(c²+d²)]i(a+bi)÷(c+di)=[(ac+bd)/(c²+d²)]+[(bc−ad)/(c²+d²)]i

4. 共轭复数

一个复数z=a+biz=a+biz=a+bi的共轭复数为a−bia−bia−bi(实部不变,虚部取反),记为 z‾=a−bi\overline{z}=a-biz=a−bi
当复数模为1时(即|z|=1),与共轭复数互为倒数
证明:z∗z‾=a2−b2∗i2=a2+b2=∣z∣2=1z*\overline{z}=a^2-b^2*i^2=a^2+b^2=|z|^2=1z∗z=a2−b2∗i2=a2+b2=∣z∣2=1

FFT加速多项式乘法

由于多项式乘法用点值表示比用系数表示快的多,所以我们先要将系数表示法转化成点值表示法相乘,再将结果的点值表示法转化为系数表示法的过程。
第一个过程叫做FFT(快速傅里叶变换),第二个过程叫IFFT(快速傅里叶逆变换)
在讲这两个过程之前,首先了解一个概念:

单位根

复数ω\omegaω满足ωn=1\omega^n=1ωn=1,称ω\omegaω是n次单位根

怎么找单位根?

单位圆:圆心为原点、1为半径的圆
单位圆n等分,取这n个点(或点表示的向量)所表示的复数(即分别以这n个点的横坐标为实部、纵坐标为虚部,所构成的虚数),即为n次单位根
下图包含了当n=8时,所有的8次单位根,分别记为ω81,ω82.....,ω88\omega_8^1,\omega_8^2.....,\omega_8^8ω81​,ω82​.....,ω88​
(图中圆的半径是1,w表示ω\omegaω,且下标8已省略)
图是我自己画的,可能有点丑QWQ

由此我们知道如何找单位根啦
从点(1,0)开始(即ωn1\omega_n^1ωn1​),逆时针将这n个点从0开始编号,第k个点对应的虚数记作ωnk\omega_n^kωnk​
由复数相乘法则:模长相乘幅角相加​ 可得:
(ωn1)k=ωnk(\omega_n^1)^k=\omega_n^k(ωn1​)k=ωnk​
根据每个复数的幅角,可以计算出所对应的点/向量。ωnk\omega_n^kωnk​ 对应的点/向量是(cos⁡kn2π,sin⁡kn2π)(cos⁡\frac kn2π,sin⁡\frac kn2π)(cos⁡nk​2π,sin⁡nk​2π),即为复数 cos⁡kn2π+i∗sin⁡kn2πcos⁡\frac kn2π+i *sin⁡\frac kn2πcos⁡nk​2π+i∗sin⁡nk​2π

单位根的性质

建议记住,因为对之后的分析很重要!!

1.ωnk=ω2n2k\omega_n^k=\omega_{2n}^{2k}ωnk​=ω2n2k​

2.ωnk=−ωnk+n2\omega_n^k=-\omega_{n}^{k+\frac n 2}ωnk​=−ωnk+2n​​

3.ωn0=ωnn=1\omega_n^0=\omega_{n}^n=1ωn0​=ωnn​=1

至于怎么证明,就是复数相乘时模长相乘幅角相加的原则。或者你直接观察图也可以很显然的得出结论。​

DFT(离散傅里叶变换)

对于任意多项式系数表示转点值表示,例如F[x]=y=a0x0+a1x1+a2x2+......+anxnF[x]=y=a_0x^0+a_1x^1+a_2x^2+......+a_nx^nF[x]=y=a0​x0+a1​x1+a2​x2+......+an​xn ,可以随便取任意n个xxx值代入计算,但这样时间复杂度是O(n2)O(n^2)O(n2)
所以伟大数学家傅里叶取了一些特殊的点代入,从而进行优化。
他规定了点值表示中的nnn个xxx为nnn个模长为1的复数。这nnn个复数不是随机的,而是单位根
把上述的n个复数(单位根)ωn0,ωn1.....,ωnn−1\omega_n^0,\omega_n^1.....,\omega_n^{n-1}ωn0​,ωn1​.....,ωnn−1​代入多项式,能得到一种特殊的点值表示,这种点值表示就叫DFT(离散傅里叶变换)

FFT(快速傅里叶变换)

虽然DFT能把多项式转换成点值但它仍然是暴力代入nnn个数,复杂度仍然是O(n2),所以它只是快速傅里叶变换的朴素版。
所以我们要考虑利用单位根的性质,加速我们的运算,得到FFT(快速傅里叶变换)
对于多项式 A(x)=a0+a1x+a2x2+...+an−1xn−1A(x)=a_0+a_1x+a_2x^2+...+a_{n−1}x^{n−1}A(x)=a0​+a1​x+a2​x2+...+an−1​xn−1
将A(x)的每一项按照下标的奇偶分成两部分:
A(x)=a0+a2x2+...+an−2xn−2+x∗(a1+a3x2+...+an−1xn−2)A(x)=a_0+a_2x^2+...+a_{n−2}x^{n−2}+x*(a_1+a_3x^2+...+a_{n−1}x^{n−2})A(x)=a0​+a2​x2+...+an−2​xn−2+x∗(a1​+a3​x2+...+an−1​xn−2)
设两个多项式 A0(x)A_0(x)A0​(x)和A1(x)A_1(x)A1​(x),令:
A0(x)=a0x0+a2x1+...+an−2xn/2−1A_0(x)=a_0x^0+a_2x^1+...+a_{n-2}x^{n/2-1}A0​(x)=a0​x0+a2​x1+...+an−2​xn/2−1
A1(x)=a1x0+a3x1+...+an−1xn/2−1A_1(x)=a_1x^0+a_3x^1+...+a_{n-1}x^{n/2-1}A1​(x)=a1​x0+a3​x1+...+an−1​xn/2−1
显然,A(x)=A0(x2)+x∗A1(x2)A(x)=A_0(x^2)+x*A_1(x^2)A(x)=A0​(x2)+x∗A1​(x2)
假设k<nk<nk<n,代入x=ωnkx=ω_n^kx=ωnk​(n次单位根):
A(ωnk)A(\omega_n^k)A(ωnk​)=A0(ωn2k)+ωnk∗A1(ωn2k)=A_0(\omega_n^{2k})+\omega_n^{k}*A_1(\omega_n^{2k})=A0​(ωn2k​)+ωnk​∗A1​(ωn2k​)
=A0(ωn2k)+ωnk∗A1(ωn2k)=A_0(\omega_\frac n2^{k})+\omega_n^{k}*A_1(\omega_\frac n 2^{k})=A0​(ω2n​k​)+ωnk​∗A1​(ω2n​k​)

A(ωnk+n2)=A0(ωn2k+n)+ωnk+n2∗A1(ωn2k+n)A(\omega_n^{k+\frac n 2})=A_0(\omega_n^{2k+n})+\omega_n^{k+\frac n 2}*A_1(\omega_n^{2k+n})A(ωnk+2n​​)=A0​(ωn2k+n​)+ωnk+2n​​∗A1​(ωn2k+n​)
=A0(ωn2k)−ωnk∗A1(ωn2k)=A_0(\omega_\frac n2^{k})-\omega_n^{k}*A_1(\omega_\frac n 2^{k})=A0​(ω2n​k​)−ωnk​∗A1​(ω2n​k​)

考虑A1(x)和A2(x)分别在(ωn21,ωn22,ωn23,...,ωn2n2−1)(\omega_\frac n 2^{1},\omega_\frac n 2^{2},\omega_\frac n 2^{3},...,\omega_\frac n 2^{\frac n 2-1})(ω2n​1​,ω2n​2​,ω2n​3​,...,ω2n​2n​−1​)的点值表示已经求出,就可以O(n)求出A(x)在(ωn1,ωn2,ωn3,...,ωnn−1)(\omega_n ^{1},\omega_n ^{2},\omega_n ^{3},...,\omega_n ^{n-1})(ωn1​,ωn2​,ωn3​,...,ωnn−1​)处的点值表示。这个操作叫蝴蝶变换
而A1(x)和A2(x)是规模缩小了一半的子问题,所以不断向下递归分治。当n=1的时候返回。
:这个过程一定要求每层都可以分成两大小相等的部分,所以多项式最高次项一定是2的幂,不是的话直接在最高次项补零QAQ。
时间复杂度O(nlog2n)O(nlog_2n)O(nlog2​n)

IFFT(快速傅里叶逆变换)

我们已经将两个多项式从系数表示法转化成点值表示法相乘后,还要将结果从点值表示法转化为系数表示法,也就是IFFT(快速傅里叶逆变换)
首先思考一个问题,为什么要把ωnk\omega_n^kωnk​(单位根)作为x代入?
当然是因为离散傅里叶变换特殊的性质,而这也和IFFT有关。
一个重要结论
把多项式A(x)的离散傅里叶变换结果作为另一个多项式B(x)的系数,取单位根的倒数即ωn0,ωn−1.....,ωn1−n\omega_n^0,\omega_n^{-1}.....,\omega_n^{1-n}ωn0​,ωn−1​.....,ωn1−n​作为x代入B(x),得到的每个数再除以n,得到的是A(x)的各项系数,这就实现了傅里叶变换的逆变换了。相当于在FFT基础上再搞一次FFT。
证明(个人觉得写的非常清楚,不想看的跳过吧)~~

设(y0,y1,y2,...,yn−1)(y_0,y_1,y_2,...,y_{n−1})(y0​,y1​,y2​,...,yn−1​)为多项式
A(x)=a0+a1x+a2x2+...+an−1xn−1A(x)=a_0+a_1x+a_2x^2+...+a_{n−1}x^{n−1}A(x)=a0​+a1​x+a2​x2+...+an−1​xn−1的离散傅里叶变换。
设多项式B(x)=y0+y1x+y2x2+...+yn−1xn−1B(x)=y_0+y_1x+y_2x^2+...+y_{n−1}x^{n−1}B(x)=y0​+y1​x+y2​x2+...+yn−1​xn−1
把离散傅里叶变换的ωn0,ωn1.....,ωnn−1\omega_n^0,\omega_n^1.....,\omega_n^{n-1}ωn0​,ωn1​.....,ωnn−1​这n个单位根的倒数,即ωn0,ωn−1.....,ωn1−n\omega_n^0,\omega_n^{-1}.....,\omega_n^{1-n}ωn0​,ωn−1​.....,ωn1−n​作为x代入B(x)B(x)B(x), 得到一个新的离散傅里叶变换(z0,z1,z2,...,zn−1)(z_0,z_1,z_2,...,z{n−1})(z0​,z1​,z2​,...,zn−1)
zkz_kzk​=∑i=0n−1yi(ωn−k)i\sum_{i=0}^{n−1}y_i(ω_n^{-k})^i∑i=0n−1​yi​(ωn−k​)i
=∑i=0n−1(∑j=0n−1aj∗(ωni)j)(ωn−k)i\sum_{i=0}^{n−1}(\sum_{j=0}^{n-1}a_j*(\omega_n^i)^j)(ω_n^{-k})^i∑i=0n−1​(∑j=0n−1​aj​∗(ωni​)j)(ωn−k​)i
=∑j=0n−1aj∗(∑i=0n−1(ωni)j−k)\sum_{j=0}^{n-1}a_j*(\sum_{i=0}^{n−1}(\omega_n^i)^{j-k})∑j=0n−1​aj​∗(∑i=0n−1​(ωni​)j−k)

当j−k=0j−k=0j−k=0时,∑i=0n−1(ωni)j−k=n\sum_{i=0}^{n−1}(\omega_n^i)^{j-k}=n∑i=0n−1​(ωni​)j−k=n
否则,通过等比数列求和可知:
∑i=0n−1(ωni)j−k\sum_{i=0}^{n−1}(\omega_n^i)^{j-k}∑i=0n−1​(ωni​)j−k=(ωnj−k)n−1ωnj−k−1\frac{(ω_n^{j−k})^n-1}{ω_n^{j−k}-1}ωnj−k​−1(ωnj−k​)n−1​=(ωnn)j−k−1ωnj−k−1\frac{(ω_n^{n})^{j-k}-1}{ω_n^{j−k}-1}ωnj−k​−1(ωnn​)j−k−1​=1−1ωnj−k−1\frac{1-1}{ω_n^{j−k}-1}ωnj−k​−11−1​=0
(因为ωnn\omega_n^nωnn​=ωn0\omega_n^0ωn0​=1)
所以
zkz_kzk​=n∗akn*a_kn∗ak​
aka_kak​=zkn\frac {z_k} nnzk​​ ,得证。

怎么求单位根的倒数呢?
单位根的倒数其实就是它的共轭复数 。不明白的可以看看前面共轭复数的介绍
到现在你已经完全学会FFT了,但写递归还是可能会超时,所以我们需要优化

优化:迭代FFT

在进行FFT时,我们要把各个系数不断分组并放到两侧,一个系数原来的位置和最终的位置的规律如下。
初始位置:ωn0\omega_n^0ωn0​ ωn1\omega_n^1ωn1​ ωn2\omega_n^2ωn2​ ωn3\omega_n^3ωn3​ ωn4\omega_n^4ωn4​ ωn5\omega_n^5ωn5​ ωn6\omega_n^6ωn6​ ωn7\omega_n^7ωn7​
第一轮后:ωn0\omega_n^0ωn0​ ωn2\omega_n^2ωn2​ ωn4\omega_n^4ωn4​ ωn6\omega_n^6ωn6​|ωn1\omega_n^1ωn1​ ωn3\omega_n^3ωn3​ ωn5\omega_n^5ωn5​ ωn7\omega_n^7ωn7​
第二轮后:ωn0\omega_n^0ωn0​ ωn4\omega_n^4ωn4​|ωn2\omega_n^2ωn2​ ωn6\omega_n^6ωn6​|ωn1\omega_n^1ωn1​ ωn5\omega_n^5ωn5​|ωn3\omega_n^3ωn3​ ωn7\omega_n^7ωn7​
第三轮后:ωn0\omega_n^0ωn0​|ωn4\omega_n^4ωn4​|ωn2\omega_n^2ωn2​|ωn6\omega_n^6ωn6​|ωn1\omega_n^1ωn1​|ωn5\omega_n^5ωn5​|ωn3\omega_n^3ωn3​|ωn7\omega_n^7ωn7​
“|”代表分组界限
把每个位置用二进制表现出来。位置x上的数,最后所在的位置是“x二进制翻转得到的数”,例如4(100)最后到了1(001)5(101)最后不变为5(101),3(011)最后到了6(110)。
所以我们先把每个数放到最后的位置上,然后不断向上还原,同时求出点值表示就可以啦。
迭代版FFT就比之前的递归版快多了,真√O(nlog2n)O(nlog_2n)O(nlog2​n)绝妙算法

代码实现FFT

下面是本人写的FFT加速高精度乘法的代码(并有详细注释):

#include<bits/stdc++.h>
using namespace std;
//complex是stl自带的定义复数的容器
typedef complex<double> cp;
#define N 2097153
//pie表示圆周率π
const double pie=acos(-1);
int n;
cp a[N],b[N];
int rev[N],ans[N];
char s1[N],s2[N];
//读入优化
int read(){int sum=0,f=1;char ch=getchar();while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}return sum*f;
}
//初始化每个位置最终到达的位置
{int len=1<<k;for(int i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数)
void fft(cp *a,int n,int flag){ for(int i=0;i<n;i++){//i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。 if(i<rev[i])swap(a[i],a[rev[i]]);}for(int h=1;h<n;h*=2)//h是准备合并序列的长度的二分之一{cp wn=exp(cp(0,flag*pie/h));//求单位根w_n^1 for(int j=0;j<n;j+=h*2)//j表示合并到了哪一位{cp w(1,0);for(int k=j;k<j+h;k++)//只扫左半部分,得到右半部分的答案{cp x=a[k];cp y=w*a[k+h];a[k]=x+y;  //这两步是蝴蝶变换 a[k+h]=x-y;w*=wn; //求w_n^k }}}//判断是否是FFT还是IFFT if(flag==-1)for(int i=0;i<n;i++)a[i]/=n;
}
int main(){n=read(); scanf("%s%s",s1,s2);//读入的数的每一位看成多项式的一项,保存在复数的实部 for(int i=0;i<n;i++)a[i]=(double)(s1[n-i-1]-'0');for(int i=0;i<n;i++)b[i]=(double)(s2[n-i-1]-'0');//k表示转化成二进制的位数 int k=1,s=2;while((1<<k)<2*n-1)k++,s<<=1;init(k);//FFT 把a的系数表示转化为点值表示 fft(a,s,1);//FFT 把b的系数表示转化为点值表示 fft(b,s,1);//FFT 两个多项式的点值表示相乘 for(int i=0;i<s;i++)a[i]*=b[i];//IFFT 把这个点值表示转化为系数表示 fft(a,s,-1);//保存答案的每一位(注意进位) for(int i=0;i<s;i++){//取实数四舍五入,此时虚数部分应当为0或由于浮点误差接近0ans[i]+=(int)(a[i].real()+0.5);ans[i+1]+=ans[i]/10;ans[i]%=10;}while(!ans[s]&&s>-1)s--;if(s==-1)printf("0");elsefor(int i=s;i>=0;i--)printf("%d",ans[i]);return 0;
}

后记

这篇博客写了一天,终于写完了,完结撒花✿✿ヽ(°▽°)ノ✿
FWT我来啦!!!

超详细易懂FFT(快速傅里叶变换)及代码实现相关推荐

  1. 如何 FFT(快速傅里叶变换) 求幅度、频率(超详细 含推导过程)

    目录 如何 FFT(快速傅里叶变换) 求幅度.频率(超详细 含推导过程) 一. 打颗栗子 二. 求幅度 1. 快速傅里叶变换 2. 求出复数的绝对值 3. 归一化 小结 三. 求频率 1. 频率公式 ...

  2. FFT快速傅里叶变换 超详细的入门学习总结

    FFT快速傅里叶变换 说明 本文创作的目的是为自己巩固该算法,加深印象并深入理解,同时也为FFT入门学者提供一份可鉴的学习总结. 原文链接:https://blog.csdn.net/qq_39565 ...

  3. 【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)

    [经典算法实现 44]理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法) 一.二维FFTFFTFFT快速傅里叶变换 公式推导 二.二维FFTFFTFFT 及 IFFTIF ...

  4. FFT快速傅里叶变换的应用——画单边频谱图matlab

    FFT快速傅里叶变换的应用--画单边频谱图matlab 快速傅里叶变换在数字信号处理里用的十分广泛,在matlab仿真中,处理信号的时频域变换十分有效,这里结合两个做过的仿真,来说一说fft的应用:画 ...

  5. FFT快速傅里叶变换C语言实现信号处理 对振动信号进行实现时域到频域的转换

    FFT快速傅里叶变换C语言实现信号处理 对振动信号进行实现时域到频域的转换,可实现FFT8192个点或改成其他FFT1024.4096等等,可以直接运行,运行结果与matlab运行的一致,写好了注释, ...

  6. c语言fft乘法步骤,C语言实现FFT(快速傅里叶变换).doc

    C语言实现FFT(快速傅里叶变换) 择蚁牙幸帆揣邓淌港烬粹甩滋整维含兔忿茂慨渔下餐随扼哇房坏鹅穆礼围引介害芝共茨恿把喜恤寇杖除冕嗓停揍猫调锚遭傀个碱晓频斌硕宾撕坪莱哩腊养掘蹄轴国繁蔬虞靡砖焙倍勾呸怀怒 ...

  7. 快速傅里叶变换c语言函数,C语言实现FFT(快速傅里叶变换)

    while(1); } #include #include /********************************************************************* ...

  8. YOLO 超详细入门02 v2 (含代码及原文)

    文章目录 前言 背景 总结 一.YOLOv2改进之框架 1.1 网络架构 1.2 Batch Normalization 二.YOLOv2改进方法之尺寸相关 2.1 High Resolution C ...

  9. fft2MATLAB内存不足,matlab中fft快速傅里叶变换

    博文来源:https://ww2.mathworks.cn/help/matlab/ref/fft.html?searchHighlight=fft&s_tid=doc_srchtitle 视 ...

最新文章

  1. python把文字矢量化_这个python函数可以被矢量化吗?
  2. 《预训练周刊》第25期:HyperCLOVA:数十亿级韩语生成式预训练变换器、GPT-3在生物医学领域不是好的小样本学习器...
  3. 20145209 2016-2017-2 《Java程序设计》第7周学习总结
  4. JavaScript性能优化 DOM编程
  5. 什么?Spring Boot CommandLineRunner 有坑!?
  6. 2021年,推荐你使用.NET 5的7大原因
  7. jtable隐藏全部_全部隐藏!
  8. 一些关于ROS中move_base的理解
  9. 计数排序vs基数排序vs桶排序
  10. java 托盘开发_java托盘开发界面记录
  11. 一些常用的场景进行描述分析(权限管理、6个典型场景)
  12. 各位,请慎用 subList!原来这么多坑!!
  13. HTML5,不只是看上去很美(可交互地铁线路图)
  14. C语言入门:查找子串
  15. pdf epub reader 比较
  16. 数字IC设计入门(3)初识电路图
  17. 最新花椒回放下载方法-马赛克视频助手
  18. 数据库:ADO是什么?
  19. 利用MCI命令进行 播放录制音乐,以及弹出光驱,音量控制获得播放进度等等操作。。。开发必备。
  20. 页面跳转传参,A 页面跳转到B页面,把A页面获取的值传到B页面

热门文章

  1. python中tracer函数意思_浅析Python编写函数装饰器
  2. 微信公众号采集,微信临时链接转永久链接
  3. 软件开发中的上游upstream
  4. LPCTSTR LPCWSTR LPCSTR
  5. 实验篇(7.2) 03. 部署物理实验环境(下)❀ 远程访问
  6. 到梦空间发布资格考试答题
  7. 运动装备有哪些?2022年值得买的运动装备分享
  8. HEU《数据库原理》期末复习总结
  9. 微信小程序last-child、first-child 样式失效的解决办法
  10. python语法糖怎么用_Python中语法糖及带参语法糖