卷积
已知两个多项式
A
(
x
)
=
a
0
+
a
1
x
1
+
a
2
x
2
+
a
3
x
3
+
⋯
+
a
n
x
n
A(x)=a_{0}+a_{1}x^1+a_{2}x^2+a_{3}x^3+\cdots +a_{n}x^n
A(x)=a0+a1x1+a2x2+a3x3+⋯+anxn
B
(
x
)
=
b
0
+
b
1
x
1
+
b
2
x
2
+
b
3
x
3
+
⋯
+
b
m
x
m
B(x)=b_{0}+b_{1}x^1+b_{2}x^2+b_{3}x^3+\cdots +b_{m}x^m
B(x)=b0+b1x1+b2x2+b3x3+⋯+bmxm
C
(
x
)
=
A
(
x
)
B
(
x
)
C(x)=A(x)B(x)
C(x)=A(x)B(x)
那么多项式C(x)得到的是一个n+m-1的多项式
C ( x ) = c 0 + c 1 x 1 + c 2 x 2 + + c 3 x 3 + ⋯ + c n + m − 1 x n + m − 1 C(x)=c_{0}+c_{1}x^1+c_{2}x^2++c_{3}x^3+\cdots +c_{n+m-1}x^{n+m-1} C(x)=c0+c1x1+c2x2++c3x3+⋯+cn+m−1xn+m−1
C ( x ) = ∑ i = 0 n + m − 1 c i x i C(x)=\sum_{i=0}^{n+m-1}c_{i}x^{i} C(x)=∑i=0n+m−1cixi
其中 c i = ∑ j = 0 i a j b i − j c_{i}=\sum_{j=0}^{i}a_{j}b_{i-j} ci=∑j=0iajbi−j
我们可以把多项式A(x),B(x),C(x)的系数以一个行向量来表示
a ⃗ = ( a 0 , a 1 , a 2 , ⋯ , a n ) \vec{a}=\left ( a_{0},a_{1},a_{2},\cdots ,a_{n} \right ) a=(a0,a1,a2,⋯,an)
b ⃗ = ( b 0 , b 1 , b 2 , ⋯ , b m ) \vec{b}=\left ( b_{0},b_{1},b_{2},\cdots ,b_{m} \right ) b=(b0,b1,b2,⋯,bm)
c ⃗ = ( c 0 , c 1 , c 2 , ⋯ , c n + m − 1 ) \vec{c}=\left ( c_{0},c_{1},c_{2},\cdots ,c_{n+m-1} \right ) c=(c0,c1,c2,⋯,cn+m−1)
通过向量 a ⃗ \vec{a} a和 b ⃗ \vec{b} b求解出 c ⃗ \vec{c} c的操作我们称为卷积
c ⃗ = a ⃗ ⨂ b ⃗ \vec{c}=\vec{a}\bigotimes \vec{b} c=a⨂b ,$\bigotimes $为卷积符号
相关算法
求卷积的算法直接模拟的话时间复杂度是 O ( n 2 ) O(n^{2}) O(n2)的,这个时候我们有一个算法来优化实现 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))的时间复杂度
就像我们前面讲的多项式的系数可以表示成行向量的形式,一个多项式也可以用这个多项式上的一些点值来表示如
比如多项式 y = A ( x ) y=A(x) y=A(x)可以用 { ( x 0 , y 0 ) , ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x n , y n ) } \left \{ (x_{0},y_{0}),(x_{1},y_{1}),(x_{2},y_{2}),\cdots ,(x_{n},y_{n}) \right \} {(x0,y0),(x1,y1),(x2,y2),⋯,(xn,yn)}这样的n个点来表示
举个具体的例子
A
(
x
)
=
x
2
−
2
x
+
3
A(x)=x^2-2x+3
A(x)=x2−2x+3
{
(
0
,
3
)
,
(
1
,
2
)
,
(
2
,
3
)
,
(
3
,
6
)
,
(
4
,
11
)
}
\left \{ (0,3),(1,2),(2,3),(3,6),(4,11) \right \}
{(0,3),(1,2),(2,3),(3,6),(4,11)}
B
(
x
)
=
x
2
+
x
+
1
B(x)=x^2+x+1
B(x)=x2+x+1
{
(
0
,
1
)
,
(
1
,
3
)
,
(
2
,
7
)
,
(
3
,
13
)
,
(
4
,
21
)
}
\left \{ (0,1),(1,3),(2,7),(3,13),(4,21) \right \}
{(0,1),(1,3),(2,7),(3,13),(4,21)}
C
(
x
)
=
A
(
x
)
B
(
x
)
=
x
4
−
x
3
+
2
x
2
+
3
C(x)=A(x)B(x)=x^4-x^3+2x^2+3
C(x)=A(x)B(x)=x4−x3+2x2+3
{
(
0
,
3
)
,
(
1
,
6
)
,
(
2
,
21
)
,
(
3
,
78
)
,
(
4
,
231
)
}
\left \{ (0,3),(1,6),(2,21),(3,78),(4,231) \right \}
{(0,3),(1,6),(2,21),(3,78),(4,231)}
从点值表达式的例子上可以看出,两个多项式的点值表达式的乘积的值与这两个多项式乘完之后的多项式的点值表达式是一致的,那么就有一个想法我们可不可以把多项式转换为点值表达式,然后通过点值表达式的乘积获得原两个多项式乘积后的多项式的点值表达式,再又这个点值表达式来求得多项式呢。这显然是可以的。
把多项式转化为点值表达式的算法为DFT(离散傅里叶变换)
把点值表达式转换为多项式的算法为IDFT(逆DFT)
利用DFT和IDFT来求解多项式的方法叫做FFT
DFT原理
我们前面说了要构建一个点值表达式,点值表达式中要求每一个
x
i
x_{i}
xi都要不同,这个时候我们就可以用到复数的性质了简化运算了
复数我们可以用指数形式来表达
z
=
a
+
i
b
z=a+ib
z=a+ib
z
=
r
e
i
θ
z=re^{iθ}
z=reiθ
这里r为复数z的模,θ为幅角度数
再回到
x
i
x_{i}
xi的取值,我们可以取
w
n
=
1
w^{n}=1
wn=1,的解作为
x
i
x_{i}
xi的取值,其中
w
n
w^{n}
wn恰是1的n次单位复根,其个数也为n个
即用指数形式表达的话为
e
2
π
k
i
n
e^{\frac{2\pi ki}{n}}
en2πki,
k
=
0
,
1
,
2
,
3
,
4
,
⋯
,
n
−
1
k=0,1,2,3,4,\cdots ,n-1
k=0,1,2,3,4,⋯,n−1,即有n个
n次单位复根有一些性质
- 相消引理
对于任意 k > 0 , d > 0 k>0,d>0 k>0,d>0,有 w d n d k = w n k w_{dn}^{dk}=w_{n}^{k} wdndk=wnk
证明:
w
n
k
=
e
2
π
k
i
n
w_{n}^{k}=e^{\frac{2\pi ki}{n}}
wnk=en2πki
w
d
n
d
k
=
e
d
2
π
k
i
n
d
=
e
2
π
k
i
n
w_{dn}^{dk}=e^{\frac{d2\pi ki}{nd}}=e^{\frac{2\pi ki}{n}}
wdndk=endd2πki=en2πki即
w
d
n
d
k
=
w
n
k
w_{dn}^{dk}=w_{n}^{k}
wdndk=wnk
2. 折半引理
如果有n>0,且n为偶数有
(
w
n
k
+
n
2
)
2
=
(
w
n
k
)
2
(w_{n}^{k+\frac{n}{2}})^{2}=(w_{n}^{k})^{2}
(wnk+2n)2=(wnk)2
证明:
(
e
2
π
(
k
+
n
2
)
i
n
)
2
(e^{\frac{2\pi (k+\frac{n}{2})i}{n}})^{2}
(en2π(k+2n)i)2=
(
e
2
π
k
i
n
e
2
π
n
i
2
n
)
2
=
(
e
2
π
k
i
n
)
2
(e^{\frac{2\pi ki}{n}}e^{\frac{2\pi ni}{2n}})^2=(e^{\frac{2\pi ki}{n}})^2
(en2πkie2n2πni)2=(en2πki)2
3. 求和引理
对于
n
>
=
1
n>=1
n>=1且
n
n
n不能整除
k
k
k,有
∑
i
=
0
n
−
1
(
w
n
k
)
i
=
0
\sum_{i=0}^{n-1}(w_{n}^{k})^i=0
∑i=0n−1(wnk)i=0
证明: ∑ i = 0 n − 1 ( w n k ) i \sum_{i=0}^{n-1}(w_{n}^{k})^i ∑i=0n−1(wnk)i是一个等比数列求和所以用等比数列求和公式有
∑ i = 0 n − 1 ( w n k ) i = 1 − ( w n k ) n 1 − w n k = 1 − ( w n n ) k 1 − w n k = 1 − ( e 2 π k i n n ) k 1 − w n k = 1 − 1 1 − w n k = 0 \sum_{i=0}^{n-1}(w_{n}^{k})^i=\frac{1-(w_{n}^{k})^{n}}{1-w_{n}^{k}}=\frac{1-(w_{n}^{n})^{k}}{1-w_{n}^{k}}=\frac{1-(e^{\frac{2\pi ki}{n}n})^{k}}{1-w_{n}^{k}}=\frac{1-1}{1-w_{n}^{k}}=0 ∑i=0n−1(wnk)i=1−wnk1−(wnk)n=1−wnk1−(wnn)k=1−wnk1−(en2πkin)k=1−wnk1−1=0
然后让我们回到 A ( x ) A(x) A(x)来
原来的
A
(
x
)
=
∑
i
=
0
n
a
i
x
i
A(x)=\sum_{i=0}^{n}a_{i}x^{i}
A(x)=∑i=0naixi那么我们现在让A(x)多增加一些项,让他的项数增加到
l
e
n
=
2
t
len=2^{t}
len=2t,
l
e
n
len
len即为大于n的2的次幂,多余的项我们可以把系数
a
i
=
0
a_{i}=0
ai=0
为了方便我们令新的
n
=
l
e
n
n=len
n=len
我们把 w n k w_{n}^{k} wnk代入 A ( x ) A(x) A(x),再将 A ( x ) A(x) A(x)的系数分为奇偶两个式子有
A 0 ( w n k ) = a 0 + a 2 w n k + a 4 ( w n k ) 2 + ⋯ + a n − 2 ( w n k ) n 2 − 1 A_{0}(w_{n}^{k})=a_{0}+a_{2}w_{n}^{k}+a_{4}(w_{n}^{k})^{2}+\cdots +a_{n-2}(w_{n}^{k})^{\frac{n}{2}-1} A0(wnk)=a0+a2wnk+a4(wnk)2+⋯+an−2(wnk)2n−1
A 1 ( w n k ) = a 1 + a 3 w n k + a 5 ( w n k ) 2 + ⋯ + a n − 1 ( w n k ) n 2 − 1 A_{1}(w_{n}^{k})=a_{1}+a_{3}w_{n}^{k}+a_{5}(w_{n}^{k})^{2}+\cdots +a_{n-1}(w_{n}^{k})^{\frac{n}{2}-1} A1(wnk)=a1+a3wnk+a5(wnk)2+⋯+an−1(wnk)2n−1
那么原式就有
A ( w n k ) = A 0 ( ( w n k ) 2 ) + w n k A 1 ( ( w n k ) 2 ) A(w_{n}^{k})=A_{0}((w_{n}^{k})^2)+w_{n}^{k}A_{1}((w_{n}^{k})^{2}) A(wnk)=A0((wnk)2)+wnkA1((wnk)2)
= A 0 ( ( e 2 π k i n ) 2 ) + w n k A 1 ( ( e 2 π k i n ) 2 ) =A_{0}((e^{\frac{2\pi ki}{n}})^2)+w_{n}^{k}A_{1}((e^{\frac{2\pi ki}{n}})^{2}) =A0((en2πki)2)+wnkA1((en2πki)2)
= A 0 ( e 2 π k i n 2 ) + w n k A 1 ( e 2 π k i n 2 ) =A_{0}(e^{\frac{2\pi ki}{\frac{n}{2}}})+w_{n}^{k}A_{1}(e^{\frac{2\pi ki}{\frac{n}{2}}}) =A0(e2n2πki)+wnkA1(e2n2πki)
= A 0 ( w n 2 k ) + w n k A 1 ( w n 2 k ) =A_{0}(w_{\frac{n}{2}}^{k})+w_{n}^{k}A_{1}(w_{\frac{n}{2}}^{k}) =A0(w2nk)+wnkA1(w2nk)
我们再利用折半原理
A ( w n k + n 2 ) = A 0 ( ( w n k + n 2 ) 2 ) + w n k + n 2 A 1 ( ( w n k + n 2 ) 2 ) A(w_{n}^{k+\frac{n}{2}})=A_{0}((w_{n}^{k+\frac{n}{2}})^2)+w_{n}^{k+\frac{n}{2}}A_{1}((w_{n}^{k+\frac{n}{2}})^{2}) A(wnk+2n)=A0((wnk+2n)2)+wnk+2nA1((wnk+2n)2)
= A ( w n k + n 2 ) = A 0 ( ( w n k ) 2 ) + w n k + n 2 A 1 ( ( w n k ) 2 ) =A(w_{n}^{k+\frac{n}{2}})=A_{0}((w_{n}^{k})^2)+w_{n}^{k+\frac{n}{2}}A_{1}((w_{n}^{k})^{2}) =A(wnk+2n)=A0((wnk)2)+wnk+2nA1((wnk)2)
= A 0 ( w n 2 k ) + w n k + n 2 A 1 ( w n 2 k ) =A_{0}(w_{\frac{n}{2}}^{k})+w_{n}^{k+\frac{n}{2}}A_{1}(w_{\frac{n}{2}}^{k}) =A0(w2nk)+wnk+2nA1(w2nk)
又有 w n k + n 2 = e 2 π ( k + n 2 ) i n = e 2 π k i n e π i = − e 2 π k i n = − w n k w_{n}^{k+\frac{n}{2}}=e^{\frac{2\pi (k+\frac{n}{2})i}{n}}=e^{\frac{2\pi ki}{n}}e^{\pi i}=-e^{\frac{2\pi ki}{n}}=-w_{n}^{k} wnk+2n=en2π(k+2n)i=en2πkieπi=−en2πki=−wnk
所以原式有
= A 0 ( w n 2 k ) − w n k A 1 ( w n 2 k ) =A_{0}(w_{\frac{n}{2}}^{k})-w_{n}^{k}A_{1}(w_{\frac{n}{2}}^{k}) =A0(w2nk)−wnkA1(w2nk)
由于
A
(
w
n
k
+
n
2
)
A(w_{n}^{k+\frac{n}{2}})
A(wnk+2n)和
A
(
w
n
k
)
A(w_{n}^{k})
A(wnk)由两个一样的多项式可以求得,这就代表说所代入的单位复根的数目也可以折半了
同样的
A
0
(
x
)
A_{0}(x)
A0(x)和
A
1
(
x
)
A_{1}(x)
A1(x)也可以通过这样的操作再分成奇偶项,这样从迭代下去,这样优化求点值表达式的话时间复杂度就变成
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
具体求法还涉及了蝴蝶操作
求完两个多项式的点值表达式以后我们再把他们相乘,就得到了新的多项式的点值表达式了,那么如何把一个点值表达式再转化为多项式呢
IDFT原理
由点值表达式求解系数可以看作是一个解n元线性方程组的过程,那么我们就得到了一个方程组
{
a
0
(
w
n
0
)
0
+
a
1
(
w
n
0
)
1
+
⋯
+
a
n
−
1
(
w
n
0
)
n
−
1
=
A
(
w
n
0
)
a
0
(
w
n
1
)
0
+
a
1
(
w
n
1
)
1
+
⋯
+
a
n
−
1
(
w
n
1
)
n
−
1
=
A
(
w
n
1
)
⋮
a
0
(
w
n
n
−
1
)
0
+
a
1
(
w
n
n
−
1
)
1
+
⋯
+
a
n
−
1
(
w
n
n
−
1
)
n
−
1
=
A
(
w
n
n
−
1
)
\left\{\begin{matrix} a_{0}(w_{n}^{0})^{0}+a_{1}(w_{n}^{0})^{1}+\cdots +a_{n-1}(w_{n}^{0})^{n-1}=A(w_{n}^{0})\\ a_{0}(w_{n}^{1})^{0}+a_{1}(w_{n}^{1})^{1}+\cdots +a_{n-1}(w_{n}^{1})^{n-1}=A(w_{n}^{1})\\ \vdots\\ a_{0}(w_{n}^{n-1})^{0}+a_{1}(w_{n}^{n-1})^{1}+\cdots +a_{n-1}(w_{n}^{n-1})^{n-1}=A(w_{n}^{n-1}) \end{matrix}\right.
⎩⎪⎪⎪⎨⎪⎪⎪⎧a0(wn0)0+a1(wn0)1+⋯+an−1(wn0)n−1=A(wn0)a0(wn1)0+a1(wn1)1+⋯+an−1(wn1)n−1=A(wn1)⋮a0(wnn−1)0+a1(wnn−1)1+⋯+an−1(wnn−1)n−1=A(wnn−1)
我们写出方程组的矩阵形式
[
(
w
n
0
)
0
(
w
n
0
)
1
⋯
(
w
n
0
)
n
−
1
(
w
n
1
)
0
(
w
n
1
)
1
⋯
(
w
n
1
)
n
−
1
⋮
⋮
⋱
⋮
(
w
n
n
−
1
)
0
(
w
n
n
−
1
)
1
⋯
(
w
n
n
−
1
)
n
−
1
]
[
a
0
a
1
⋮
a
n
−
1
]
=
[
A
(
w
n
0
)
A
(
w
n
1
)
⋮
A
(
w
n
n
−
1
)
]
\begin{bmatrix} (w_{n}^{0})^{0} & (w_{n}^{0})^{1} &\cdots & (w_{n}^{0})^{n-1}\\ (w_{n}^{1})^{0} & (w_{n}^{1})^{1} &\cdots & (w_{n}^{1})^{n-1}\\ \vdots & \vdots & \ddots & \vdots \\ (w_{n}^{n-1})^{0} & (w_{n}^{n-1})^{1} &\cdots & (w_{n}^{n-1})^{n-1} \end{bmatrix}\begin{bmatrix} a_{0}\\ a_{1}\\ \vdots \\ a_{n-1} \end{bmatrix}=\begin{bmatrix} A(w_{n}^{0})\\ A(w_{n}^{1})\\ \vdots \\ A(w_{n}^{n-1}) \end{bmatrix}
⎣⎢⎢⎢⎡(wn0)0(wn1)0⋮(wnn−1)0(wn0)1(wn1)1⋮(wnn−1)1⋯⋯⋱⋯(wn0)n−1(wn1)n−1⋮(wnn−1)n−1⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡A(wn0)A(wn1)⋮A(wnn−1)⎦⎥⎥⎥⎤
我们可以求解
D
=
[
(
w
n
0
)
0
(
w
n
0
)
1
⋯
(
w
n
0
)
n
−
1
(
w
n
1
)
0
(
w
n
1
)
1
⋯
(
w
n
1
)
n
−
1
⋮
⋮
⋱
⋮
(
w
n
n
−
1
)
0
(
w
n
n
−
1
)
1
⋯
(
w
n
n
−
1
)
n
−
1
]
D=\begin{bmatrix} (w_{n}^{0})^{0} & (w_{n}^{0})^{1} &\cdots & (w_{n}^{0})^{n-1}\\ (w_{n}^{1})^{0} & (w_{n}^{1})^{1} &\cdots & (w_{n}^{1})^{n-1}\\ \vdots & \vdots & \ddots & \vdots \\ (w_{n}^{n-1})^{0} & (w_{n}^{n-1})^{1} &\cdots & (w_{n}^{n-1})^{n-1} \end{bmatrix}
D=⎣⎢⎢⎢⎡(wn0)0(wn1)0⋮(wnn−1)0(wn0)1(wn1)1⋮(wnn−1)1⋯⋯⋱⋯(wn0)n−1(wn1)n−1⋮(wnn−1)n−1⎦⎥⎥⎥⎤的逆矩阵
可得
D
−
1
=
1
n
[
(
w
n
−
0
)
0
(
w
n
−
0
)
1
⋯
(
w
n
−
0
)
n
−
1
(
w
n
−
1
)
0
(
w
n
−
1
)
1
⋯
(
w
n
−
1
)
n
−
1
⋮
⋮
⋱
⋮
(
w
n
−
(
n
−
1
)
)
0
(
w
n
−
(
n
−
1
)
)
1
⋯
(
w
n
−
(
n
−
1
)
)
n
−
1
]
D^{-1}=\frac{1}{n}\begin{bmatrix} (w_{n}^{-0})^{0} & (w_{n}^{-0})^{1} &\cdots & (w_{n}^{-0})^{n-1}\\ (w_{n}^{-1})^{0} & (w_{n}^{-1})^{1} &\cdots & (w_{n}^{-1})^{n-1}\\ \vdots & \vdots & \ddots & \vdots \\ (w_{n}^{-(n-1)})^{0} & (w_{n}^{-(n-1)})^{1} &\cdots & (w_{n}^{-(n-1)})^{n-1} \end{bmatrix}
D−1=n1⎣⎢⎢⎢⎡(wn−0)0(wn−1)0⋮(wn−(n−1))0(wn−0)1(wn−1)1⋮(wn−(n−1))1⋯⋯⋱⋯(wn−0)n−1(wn−1)n−1⋮(wn−(n−1))n−1⎦⎥⎥⎥⎤
我们把系数矩阵两边同时乘上一个
D
−
1
D^{-1}
D−1有
1
n
[
(
w
n
−
0
)
0
(
w
n
−
0
)
1
⋯
(
w
n
−
0
)
n
−
1
(
w
n
−
1
)
0
(
w
n
−
1
)
1
⋯
(
w
n
−
1
)
n
−
1
⋮
⋮
⋱
⋮
(
w
n
−
(
n
−
1
)
)
0
(
w
n
−
(
n
−
1
)
)
1
⋯
(
w
n
−
(
n
−
1
)
)
n
−
1
]
[
A
(
w
n
0
)
A
(
w
n
1
)
⋮
A
(
w
n
n
−
1
)
]
=
[
a
0
a
1
⋮
a
n
−
1
]
\frac{1}{n}\begin{bmatrix} (w_{n}^{-0})^{0} & (w_{n}^{-0})^{1} &\cdots & (w_{n}^{-0})^{n-1}\\ (w_{n}^{-1})^{0} & (w_{n}^{-1})^{1} &\cdots & (w_{n}^{-1})^{n-1}\\ \vdots & \vdots & \ddots & \vdots \\ (w_{n}^{-(n-1)})^{0} & (w_{n}^{-(n-1)})^{1} &\cdots & (w_{n}^{-(n-1)})^{n-1} \end{bmatrix}\begin{bmatrix} A(w_{n}^{0})\\ A(w_{n}^{1})\\ \vdots \\ A(w_{n}^{n-1}) \end{bmatrix}=\begin{bmatrix} a_{0}\\ a_{1}\\ \vdots \\ a_{n-1} \end{bmatrix}
n1⎣⎢⎢⎢⎡(wn−0)0(wn−1)0⋮(wn−(n−1))0(wn−0)1(wn−1)1⋮(wn−(n−1))1⋯⋯⋱⋯(wn−0)n−1(wn−1)n−1⋮(wn−(n−1))n−1⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡A(wn0)A(wn1)⋮A(wnn−1)⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥⎥⎤
这就是相当于把 w n i w_{n}^{i} wni变成 w n − i w_{n}^{-i} wn−i再做一次DFT,然后每个结果再除于n
给出FFT的模板
const int MAXN = 2e5+5;
struct complex {
double a, b;
complex(double aa = 0.0, double bb = 0.0) { a = aa; b = bb; }
complex operator +(const complex &e) { return complex(a + e.a, b + e.b); }
complex operator -(const complex &e) { return complex(a - e.a, b - e.b); }
complex operator *(const complex &e) { return complex(a * e.a - b * e.b, a * e.b + b * e.a); }
}x1[MAXN], x2[MAXN], x[MAXN];
void change(complex *y, int len){
int i, j, k;
for(i=1,j=len/2; i<len-1; i++){
if(i < j) swap(y[i], y[j]);
k = len/2;
while(j >= k){ j-=k; k>>=1; }
if(j < k) j += k;
}
}
void fft(complex *y, int len, int on){
change(y, len);
for(int h=2; h<=len; h<<=1){
complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
for(int j=0; j<len; j+=h){
complex w(1, 0);
for(int k=j; k<j+h/2; k++){
complex u = y[k], t = w*y[k+h/2];
y[k] = u + t, y[k+h/2] = u- t, w = w*wn;
}
}
}
if(on == -1) for(int i=0; i<len; i++) y[i].a /= len;
}
FFT可以来求解大数相乘的问题,一个整数可以拆成n位多项式相加的形式就可以用FFT来求出相乘以后的系数了
如HDU 1402
求两个整数的乘积,两个数的长度有50000位
思路
一个n位数可以拆成
a
0
+
a
1
x
1
+
a
2
x
2
+
⋯
+
a
n
x
n
a_{0}+a_{1}x^1+a_{2}x^{2}+\cdots+a_{n}x^n
a0+a1x1+a2x2+⋯+anxn的形式
x就可以认为是10,那么两个数相乘就符合卷积的运算所以可以用FFT进行运算
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <math.h>
#define PI acos(-1)
using namespace std;
typedef long long ll;
const int MAXN = 2e5+5;
struct complex {
double a, b;
complex(double aa = 0.0, double bb = 0.0) { a = aa; b = bb; }
complex operator +(const complex &e) { return complex(a + e.a, b + e.b); }
complex operator -(const complex &e) { return complex(a - e.a, b - e.b); }
complex operator *(const complex &e) { return complex(a * e.a - b * e.b, a * e.b + b * e.a); }
}x1[MAXN], x2[MAXN], x[MAXN];
void change(complex *y, int len){
int i, j, k;
for(i=1,j=len/2; i<len-1; i++){
if(i < j) swap(y[i], y[j]);
k = len/2;
while(j >= k){ j-=k; k>>=1; }
if(j < k) j += k;
}
}
void fft(complex *y, int len, int on){
change(y, len);
for(int h=2; h<=len; h<<=1){
complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
for(int j=0; j<len; j+=h){
complex w(1, 0);
for(int k=j; k<j+h/2; k++){
complex u = y[k], t = w*y[k+h/2];
y[k] = u + t, y[k+h/2] = u- t, w = w*wn;
}
}
}
if(on == -1) for(int i=0; i<len; i++) y[i].a /= len;
}
char str1[MAXN],str2[MAXN];
long long ans[MAXN];
int main()
{
while(scanf("%s%s",str1,str2)!=EOF)
{
memset(x1,0,sizeof(x1));
memset(x2,0,sizeof(x2));
memset(x,0,sizeof(x));
int len1=strlen(str1);
int len2=strlen(str2);
for(int i=0;i<len1;i++)
{
x1[len1-1-i].a=str1[i]-'0';
x1[len1-1-i].b=0;
}
for(int i=0;i<len2;i++)
{
x2[len2-1-i].a=str2[i]-'0';
x2[len2-1-i].b=0;
}
int len=1;
while((len<<=1)<(len1+len2));
fft(x1,len,1);
fft(x2,len,1);
for(int i=0;i<len;i++)
x[i]=x1[i]*x2[i];
fft(x,len,-1);
memset(ans,0,sizeof(ans));
for(int i=0;i<len;i++)
ans[i]=(ll)(x[i].a+0.5);
for(int i=0;i<len;i++)
{
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
int temp=0;
for(int i=len-1;i>=0;i--)
if(ans[i]!=0)
{
temp=i;
break;
}
for(int i=temp;i>=0;i--)
putchar(ans[i]+'0');
puts("");
}
return 0;
}