1. 定义
复数是形如 \(a+bi\) 的数,其中 \((a,b)\in \mathbb{R}\)。
- 复平面:我们把复数 \(a+bi\) 看作二维平面上的一个点 \((a,b)\),这个二维平面称为复平面。复平面上的点和复数一一对应,如同数轴上的点一一对应一样。以下我们有时会以 \((a,b)\) 指代 \(a+bi\)。
- 复数 \((a,b)\) 可以视作复平面上一个 \((0,0)\rightarrow (a,b)\) 的向量。
记 \(\text{Re} (a,b)=a,\text{Im (a,b)}=b\),可以理解为实部和虚部。
2. 基本性质与运算
复数最基本的代数运算有两条:
- 加法:\((a,b)+(c,d)=(a+b,c+d)\)。
- 乘法:\((a,b)\times (c,d)=(ac-bd,ad+bc)\)。
可以说明:加法的单位元有且仅有 \((0,0)\),乘法的单位元有且仅有 \((1,0)\)。
复数相反数的定义是自然的,也就是 \(z=(a,b)\) 的加法逆元为 \(-z=(-a,-b)\)。
除了 \((0,0)\),一切复数皆存在乘法逆元:对于实数 \((a,b)\),解方程 \((a,b)(u,v)=(1,0)\) 后解得 \(u=\frac{a}{a^2+b^2},v=-\frac{b}{a^2+b^2}\);
换言之 \(z=(a,b)\) 的逆元即为 \(z^{-1}=(\frac{a}{a^2+b^2},-\frac{b}{a^2+b^2})\)。
容易说明 \(zz^{-1}=1\) 以及 \((z^{-1})^{-1}=z\) 两条性质。
由逆元的存在,我们可以定义复数的减法以及除法运算:
-
减法: \((a,b)-(c,d)=(a-b,c-d)\)。
-
除法:\(\frac{(a,b)}{(c,d)}=(a,b)(\frac{c}{c^2+d^2},-\frac{d}{c^2+d^2})=(\frac{ac+bd}{c^2+d^2},\frac{bc-ad}{c^2+d^2})\)。
复数的很多代数性质和实数是一样的:加法交换律,结合律;乘法交换律,结合律,分配律全部满足。
有一类复数对比较特殊:它们形如 \(z=(a,b)\) 以及 \(\overline{z}=(a,-b)\),我们称这样的复数是共轭复数。
共轭复数的特殊之处在于良好的运算性质:\(z+\overline{z}=(2a,0)\) 以及 \(z-\overline{z}=(0,2b)\),对于乘法,则有 \(z\times \overline{z}=(a^2+b^2,0)\),由此我们可以看出很重要的一条性质:
共轭复数的良好性质也让我们不用死记硬背 \(\frac{(a,b)}{(c,d)}\) 的公式了,因为我们可以上下同乘 \((c,-d)\)。事实上利用这个想法(而不是先记忆复数逆再去乘法)去做复数除法效率是很快的。
3. 几何意义初探
现在我们回到定义一节中复平面的相关内容上。
我们可以自然地把一个复数看作一个向量 \(z=(0,0)\rightarrow (a,b)\),我们知道向量有模长 \(|z|\),很显然,有 \(|z|=\sqrt{a^2+b^2}\)。
回顾复数的加法运算,我们会发现其与向量的加法运算是完全一致的,而我们又知道向量相加遵循三角形定则,根据两点之间直线最短,我们导出:
这个式子取等,当且仅当 \(a,b\) 平行。
我们知道三角形不等式还有减法版本,在这里一样成立:
取等条件同上。
我们可以递归地证明:\(|a_1+a_2+...+a_n|\le |a_1|+|a_2|+...+|a_n|\)。
4. 再谈几何意义
复数的加法拥有良好的几何意义,那么复数的乘法呢?
在继续谈论复数之前,我们需要一些基础的代数知识。
欧拉公式(\(\text{Eular's formula}\)):
证明其实很容易(我不确定是否属于后证前,但这个是我所了解的比较方便的证明方式?),我们把三项全部泰勒展开:
一一比对 \([\theta^n]\) 的系数,容易发现确实是相等的。
欧拉公式比较著名的形式是 \(\theta = \pi\)(实际上,也就是 \(\cos\theta\) 以及 \(\sin \theta\) 比较简单的时候),此时有 \(e^{i\pi}+1=0\)。
现在我们回到复数上,我们前面利用平面直角坐标系定义了复平面上 \(z=(a,b)\) 的位置,现在我们换用极坐标系来定义,则每个复数 \(z\) 都可以看作从原点 \((0,0)\),以角度 \(\theta\) 引一条射线,距离原点距离为 \(r\) 的位置,其中 \(r\) 为模长 \(|z|\)。
这里显然有 \(r\ge 0\) 且 \(r\in \mathbb{R}\),不过我们知道弧度坐标制里 \(\theta\) 可以是任意大的,因为 \(\theta\) 和 \(\theta+2\pi\) 都是一个角。为了规范,我们认为 \(\theta\in (-\pi,\pi]\),对于复数 \(z\),我们把它的这个 \(\theta\) 记作 \(\text{Arg}(z)\),而把集合 \(\{\theta+2k\pi \mid k\in \mathbb{Z}\}\) 记作 \(\arg(z)\)。
首先我们知道 \(x=r\times \cos \theta\) 以及 \(y=r\times \sin \theta\) 成立,所以有复数 \(a+bi=r\times (\cos \theta + i\times \sin \theta)\) 成立。
观察 \(r\) 后面的内容,我们意识到这恰好出现在了欧拉公式中。
也就是有 \(a+bi=r\times e^{i\theta}\),这是一种全新的,简洁的表示复数的形式。
对于两个复数: \(z_1=r_1\times e^{i\theta_1},z_2=r_2\times e^{i\theta_2}\),那么将它们相乘,我们将看到: \(z_1\times z_2=(r_1\times r_2)\times e^{i(\theta_1+\theta_2)}\) 。
观察新的复数,其模长为 \(r_1\times r_2\),其角度变为 \(\theta_1+\theta_2\)。
由此,我们总结出了复数相乘的几何意义,也就是所谓的 ”模长相乘,幅角相加“,也就是 \(|a\times b|=|a|\times |b|,\text{arg}(|a|\times |b|)=\text{arg}(|a|)+\text{arg}(|b|)\)。
事实上证明这件事情并不只有这一条路,然而这个方法几乎是从代数角度完美的证明了一种几何意义。
5. 几何意义说开去
我们如何描述一个圆?圆的两要素是圆心以及半径,我们先从圆心是原点的情况说开去。
显然,设半径为 \(r\),则该圆上的所有点和 \(\{z=re^{i\theta} \mid \theta\in (-\pi,\pi]\}\) 一一对应。
更一般的呢?如果我们的圆心是一个任意点,我们记作 \(z_0\),则我们会看到该圆上所有点和 \(\{z=z_0+re^{i\theta} \mid \theta\in (-\pi,\pi]\}\) 一一对应。
三角函数有很多公式,比如和差化积,比如二倍角,三倍角。我们在学习的时候通常不学习其证明,而且一段时间不用还经常会出现忘记的情况。
事实上,利用欧拉公式,我们是可以直接推出这些公式的。
观察倒数第二行的四项:前两项是实数,后两项是虚数;最后一行中,第一项是实数,第二项是虚数,由此,我们推出:
这就是我们所熟知的和角公式;差角公式当然是一样的推法。
现在我们来看倍角公式,首先要知道的是棣莫佛公式(\(\text{de Moivre's Fomula}\)):
证明其实非常简单:
将两边的 \(e^x\) 通过欧拉公式展开即可。
令 \(n=2\),则有:
依旧是利用实部和虚部的不同,我们得到:
6. 单位根
接下来是最后一节,我们将阐述单位根相关的一些内容。
我们定义根的概念:对于一个复数 \(z_0=r_0e^{i\theta_0}\),我们称 \(z\) 是 \(r_0\) 的 $\text{n-th root} $(不会翻译了)当且仅当 \(z^n=z_0\) 成立。
根据代数基本定理我们都知道这样的 \(z\) 会有 \(n\) 个。
如何找到所有的 \(z\)?设 \(z=re^{i\theta}\),则其是我们要找的根,当且仅当:
所以所有的根,它们的 \(r\) 都应该是 \(\sqrt[n]{r_0}\)(注意 \(r\) 永远是非负的),而 \(\theta\) 形如 \(\frac{\theta_0}{n}+\frac{2k\pi}{n}(k\in \mathbb{Z})\)。
而本质不同的 \(z\) 也确实只有 \(n\) 个,也就是 \(k\in [1,n]\) 的时候的 \(n\) 个根。
因此:
对于任何的 \(z\) 和 \(n\),我们都能找到 \(z\) 的 \(n\) 个根。当 \(z=1\) 的时候,这 \(n\) 个根是最特殊的,我们称其(\(x^n=1\) 的所有解)为 \(n\) 次单位根:
我们设 \(z_0\) 的第 \(k\) 个根是 \(c_k(k\in [0,n)\)。则显然有:
单位根的一些代数性质(单位根本身是 \(e\) 的若干次幂,因此单位根的运算有良好性质):
- \(\omega_n^k=(\omega_n^1)^k\)。
- \((\omega_n^1)^2=(\omega_{n/2}^1)\),这是因为 \(\exp(i\frac{2k\pi}{n})^2=\exp(i\frac{2k\pi}{n/2})\)。
我们可以把单位根 \(\omega_{n}^{k}\) 的下标看作是模 \(n\) 意义下的,那么就有 \(\omega_{n}^{x}\omega_{n}^{y}=\omega_{n}^{x+y}\)。
Bonus:快速傅里叶变换
其实 \(\text{FFT}\) 是老生常谈的东西了,但还是觉得不彻底推一次就不太合适。
\(\text{FFT}\) 的基本思想:
考虑两个系数形式的多项式 \(A(x),B(x)\),不妨设它们的次数较大的是 \(n\)。
我们计算 \(A\times B\) 的系数形式,直接做是 \(O(n^2)\) 的。
如果我们能在低于 \(O(n^2)\) 的时间内求出 \(A(x)\) 和 \(B(x)\) 在至少 \(2n+1\) 个点处的值,然后分别相乘,就得到了 \((A\times B)(x)\) 在这 \(\ge 2n+1\) 个点处的值,然后我们再设法插值回去,得到那个唯一的多项式。
\(\text{FFT}\) 的核心,就是快速求出那若干个点值,以及快速插值回去的方法。
离散傅里叶变换 (\(\text{Discrete Fourier Transform,DFT}\))
设 \(N\) 是大于等于 \(2n+1\) 的一个数,且 \(N=2^m\),显然最小的 \(N\) 是 \(O(n)\) 级别的。
具体来说,我们计算 \(A\)(\(B\) 同理)在 \(\omega_{N}^{0},\omega_{N}^{1},...,\omega_{N}^{N-1}\) 这 \(N\) 个点的点值。
我们把下标按照奇偶分类:
这样的话,\(i\) 的下标被分治了,但是问题是,我们每次分治以后,还是要对 \(\omega_{N}^{0}\sim \omega_{N}^{N-1}\) 都去求值,这样时间复杂度其实没有任何变化,因此我们递归的次数是 \(T(N)=2T(\frac{N}{2})+O(1)=O(N)\) 级别的,而每次都要去算 \(N\) 个点值。
如果我们能做到:分治下去以后,两边分别只算 \(\frac{N}{2}\) 个点值,这样时间复杂度就真的变成了 \(T(N)=2T(\frac{N}{2})+O(1)=O(N\log N)\) 了。我们仔细观察两个和式中的单位根,我们会发现:
在第 \(\text{6}\) 节已经提及这件事情!
现在,我们的式子变成了:
我们会注意到我们把关于 \(\omega_{N}^k\) 的转变成了关于 \(\omega_{N/2}^{k}\) 的;那么,当 \(k\ge N/2\) 的时候,其实就等价于转变成了关于 \(\omega_{N/2}^{k-N/2}\) 的。具体而言,\(A(\omega_{N}^{k})\) 和 \(A(\omega_{N}^{k+N/2})\) 应该是相近的(\(k\in [0,\frac{N}{2})\):
注意到有 \(\omega_{N}^{k+N/2}=-\omega_{N}^{k}\),值得一提的是这只在 \(N\) 是偶数的时候成立。
总而言之,我们在 \(O(N\log N)\) 的时间复杂度内完成了 \(\text{DFT}\)。
傅里叶逆变换(\(\text{IDFT}\))
现在问题变成了快速插值,亦即:给出 \(N\) 个点,试还原出一个 \(N-1\) 次的唯一多项式。
这相当于解线性方程组:
记左边的这个矩阵是 \(U\),也就是 \(U_{i,j}=\omega_{N}^{ij}\)。
记矩阵 \(V\) 是满足 \(V_{i,j}=\omega_{N}^{-ij}\) 的矩阵,则两个方阵相乘,\(V\times U=E\),的结果非常特殊:
当 \(i=j\) 时,\(E_{i,j}=N\),否则根据等比数列求和,分子是 \(1-\omega_{N}^{N(j-i)}=0\),所以 \(E=nI\)。
因此如果我们在两边同时左乘 \(V\),会得到:
所以我们只需要算出向量 \(V\times A\),然后每一项都除以 \(n\),就是我们想要的结果了。
而 \(V\times A\) 实质上就是把 \(\text{DFT}\) 的过程中所有的 \(\omega_{N}^{i}\) 换成了 \(\omega_{N}^{-i}\),我们令 \(\phi_{N}^{i}=\omega_{N}^{-i}\),则 \(\phi_N\) 同样满足:
-
\(\phi_{N}^0\sim \phi_{N}^{N-1}\) 互不相同。
-
\(\phi_{N}^{2i}=\phi_{N/2}^{i}\)
-
\(\phi_{N}^{N}=1\)
-
\(\phi_{N}^{n/2+k}=-\phi_{N}^{k}\)
这些是我们能进行 \(\text{DFT}\) 的基础,既然全部满足,那么也就可以成功在 \(O(n\log n)\) 的时间内进行 \(\text{IDFT}\)。
位逆序置换
写出递归版本的 \(\text{FFT}\) 并不难,问题就在于这样常数太大了。
把递归换成迭代版本是一个不错的选择,问题在于如果我们想”自下而上“地使用迭代实现,就必须预处理出每个 \(a_i\) 在最终状态被分配到了哪里。
实质上,最后的 \(b_i\) 应该是 \(a_{p_i}\),\(p_i\) 是把 \(i\) 的二进制表示反转(不是 \(\text{0/1}\) 互换,是从后往前读的意思)后的结果。
显然 \(p\) 是可以 \(O(N)\) 求的:我们从小到大求 \(p\),那么根据 \(p_{\left\lfloor \frac{n}{2} \right\rfloor}\) 的结果,我们可以得到 \(n\) 除了最低位以外,其余位反转的结果,所以我们只需要知道最低位反转的结果即可。
for(int i=0;i<N;i++){
p[i]=p[i>>1]>>1;
if(i&1)p[i]|=(N>>1)
}
然后我们令 \(b_i=a_{p_i}\),对 \(b\) 直接进行 \(\text{DFT}\) 即可。这样我们的 \(\text{FFT}\) 常数会大大降低。
快速数论变换(\(\text{Fast Number-Theoretic Transform,NTT}\))
我们可以在取模意义下实现 \(\text{FFT}\)。
我们前面探讨了 \(\text{IDFT}\) 想要和 \(\text{DFT}\) 一样做,\(\phi\) 需要满足的条件。那么我们此时不光要满足这些条件(这样你能做 \(\text{DFT}\)),还应该满足 \(E_{i,j}=n\times [i=j]\) 的条件(这样我们能把 \(\text{IDFT}\) 转成 \(\text{DFT}\))。
可以验证的是,当 \(p\) 是形如 \(k\times 2^n+1\) 的素数时,取 \(p\) 的一原根 \(g\),令 \(\omega_{n}^{1}=g^k\),此时我们的”单位根“是满足上面所有条件的。
最常见的情形是 \(p=998244353\),此时有 \(g=3\) 是 \(p\) 的原根。
我们还可以拓展到任意模数的情况(\(\text{MTT}\))。首先注意到,如果两个长度为 \(n\) 的多项式在模 \(p\) 意义下相乘,则每一项的系数不会超过 \(n(p-1)^2\),我们选取若干个 \(\text{NTT}\) 模数,它们的积大于 \(n(p-1)^2\),设有 \(k\) 个,我们做 \(k\) 次 \(\text{NTT}\),最后利用 \(\text{CRT}\) 合并每一项的 \(k\) 个结果即可。