Fine-Grained学习笔记(1):卷积,FFT与花式字符串匹配
Fine-Grained,在算法复杂度理论中特指,对各类算法的复杂度,进行(相较于P与NP的粗粒度分类的)细粒度分类,例如,证明某问题存在 $n^2/ \log n$ 的算法.Fine-Grained是一个新兴领域,其研究前景可看作是计算机科学学科中的石墨烯与钙钛矿(误).
本系列主要参考University of Illinois at Urbana-Champaign的Timothy M. Chan在2022年秋季的课程,该课程的课堂笔记是公开的:CS 598 TMC, Fall 2022 (illinois.edu)
问题:卷积
给定长度为 $n$ 的序列 $A=<a_0,a_1,\cdots,a_{n-1}>,B=<b_0,b_1,\cdots,b_{n-1}>$,计算序列$C=<c_0,c_1,\cdots,c_{n-1}>$,其中$c_i=\Sigma_{k=0}^{i}a_kb_{i-k}$
等价问题:
给定两个多项式
$A(x)=a_{n-1}x^{n-1}+a_{n-2}x^{n-2}+\cdots+a_0$
$B(x)=b_{n-1}x^{n-1}+b_{n-2}x^{n-2}+\cdots+b_0$
计算$C(x)=A(x) \cdot B(x)=c_{2n-2}x^{2n-2}+\cdots+c_0$
朴素的算法:暴力,复杂度为$O(n^2)$
有无更好的办法?
Karatsuba算法(1960)
热身:
当$n=2$时,已知$a_0,a_1,b_0,b_1$,计算
$c_0=a_0b_0$
$c_1=a_1b_0+a_0b_1$
$c_2=a_1b_1$
这种方法需要4做4次乘法,但如果考虑
$c_1=(a_1+a_0)(b_1+b_0)-a_0b_0-a_1b_1$
便只需要做三次乘法.
那么对于任意$n$,使用分治法,将$A(x),B(x)$写成如下形式:
$A(x)=A_1(x)x^{n/2}+A_0(x)$
$B(x)=B_1(x)x^{n/2}+B_0(x)$
$A(x)B(x)=A_1(x)B_1(x)x^n+(A_1(x)B_0(x)+A_0(x)B_1(x))x^{n/2}+A_0(x)B_0(x)$
记$T(n)$为以该算法进行卷积运算的复杂度
$T(n)=3T(n/2)+O(n)$
$T(n)=O(n^{\log_23})\leq O(n^{1.59})$
Toom&Cook算法(1963)
热身:考虑$n=3$的情况
给定$a_0,a_1,a_2,b_0,b_1,b_2$,计算
$c_0=a_0b_0$
$c_1=a_0b_1+a_1b_0$
$c_2=a_0b_2+a_1b_1+a_2b_0$
$c_3=a_1b_2$
$c_4=a_2b_2$
共计需要9次乘法
更优的方法:令
$d_0=a_0b_0$
$d_1=(a_2+a_1+a_0)(b_2+b_1+b_0)$
$d_2=(4a_2+2a_1+a_0)(4b_2+2b_1+b_0)$
$d_3=(9a_2+3a_1+a_0)(9b_2+3b_1+b_0)$
$d_4=(16a_2+4a_1+a_0)(16b_2+4b_1+b_0)$
实际上,若令$d_k=(k^2a_2+ka_1+a_0)(k^2b_2+kb_1+b_0)$
$d_k=c_4k^4+c_3k^3+c_2k^2+c_1k+c_0 \qquad (k=0,\cdots 4)$
以高斯消元即可求得$c_0,\cdots,c_4$,该方法仅需5次乘法即可求解$n=3$的卷积
对于任意的$n$,利用上述方法进行三路分治,记运行时间为$T(n)$
$T(n)=5T(n/3)+O(n)$
$T(n)=O(n^{\log_3 5}) \leq O(n^{1.41})$
推广的分治法
总结上述两个算法,可以得到规律:规模为$r$的卷积求解问题,可以仅用$(2r-1)$次乘法求解.那么考虑对于规模为$n$的问题,对其进行$r$路分治求解,记其时间复杂度为$T(n)$
$T(n)=(2r-1)T(n/r)+O(n)$
$T(n)=O(n^{\log_r (2r-1)})$
$ \leq O(n^{\frac{\log(2r)}{\log r}})$
$ \leq O(n^{1+\frac{1}{\log r}})$
$ \leq O(n^{1+\epsilon})$
Cooley&Tukey 算法(快速傅立叶变换,FFT)
略
博主注:FFT虽然能够将卷积的复杂度降低到$O(n \log n)$,但运算过程中涉及了复变三角函数,必须假定无限精度实数的运算能在O(1)时间内完成,但此假设在计算复杂度领域内常用的RAM模型中并不成立,该算法的存在能否证明卷积运算的复杂度是$O(n \log n)$,此处存疑.
应用:$n$位大整数乘法
应用:3SUM问题(元素限制为有限整数)
记$[a,b]=\{a,a+1,\cdots,b\}$,即$a,b$之间的全体整数
记$[U]=[0,U]$,即$0,U$之间的全体整数.
给定$A,B,C\subseteq [U]$,$|A|,|B|,|C|=O(n)$,判断是否存在$a\in A,b\in B,c\in C$,使得$a+b=c$
朴素算法:
暴力,O(n^3)
尺取法:
稍微好一点,将$A,B$进行排序,定义两个指针分别指向$A$中最小值和$B$中最大值,比目标值小就把指向$A$的指针往右挪,否则就把指向$B$的指针往左挪,这样,对于每个$c\in C$,只需要做$O(n)$次操作.总时间复杂度$O(n^2)$
卷积法:
定义函数$f_a=\left\{\begin{matrix}
1 & a\in A \\
0 & a \notin A
\end{matrix}\right.$
$g_b=\left\{\begin{matrix}
1 & b\in B \\
0 & b \notin B
\end{matrix}\right.$
计算$h_c=\Sigma_{a=0}^{U-1}f_ag_{c-a}$,这一步卷积可以在$O(U \log U)$时间内解决,当$U \ll n^2$时,该方法较优.
应用:带通配符的字符串匹配
记$\Sigma$为字母表,给定模式串$P=p_1\cdots p_m \in (\Sigma \cup \{'?'\} )^*$,文本串$T=t_1\cdots t_n \in (\Sigma \cup \{'?'\} )^*,(m<n)$,判断模式串是否出现于文本串中,用符号表示:
$\exists i \in [n], \forall j \in [m], p_j = t_{i+j}$ 或$p_j='?'$或$t_{i+j}='?'$
朴素算法:
暴力,$O(mn)$
KMP算法:
预处理模式串,寻找每个前缀字符串的后缀字符串,用于在失配发生时,跳过文本串中没必要匹配的部分.复杂度$O(n)$,但不能解决通配符的情况.
将字母表$\Sigma$中的所有字母映射到$[1:|\Sigma|]$中的数字,以便进行数学运算.将通配符$'?'$映射为$0$
卷积法:
考虑如下的匹配函数序列:
$f_i=\Sigma_{j=1}^{m}(p_j \cdot t_{i+j} \cdot (p_j - t_{i+j})^2)$
其意义是,若模式串第$j$位和文本串第$i+j$位匹配上(包括存在通配符和两个字符相等),则该单项值为$0$,否则为正数,若求和为0,则说明文本串从第$i$位开始和模式串匹配上了.
接下来将该表达式展开
$f_i=\Sigma_{j=1}^m(p_j^3t_{i+j})-2\Sigma_{j=1}^m(p_j^2t_{i+j}^2)+\Sigma_{j=1}^m(p_jt_{i+j}^3)$
将字符串$P$反转,记为$Q$,即$q_j=p_{m+1-j}$,表达式可写为如下形式
$f_i=\Sigma_{j=1}^m(q_{m+1-j}^3t_{i+j})-2\Sigma_{j=1}^m(q_{m+1-j}^2t_{i+j}^2)+\Sigma_{j=1}^m(q_{m+1-j}t_{i+j}^3)$
可以看出是三次卷积.
时间复杂度$O(n\log n)$
时间复杂度还可以优化到$O(n \log m)$,具体方法是将文本串$T$按照每块$m$个元素划分为$n/m$块,进行$n/m$次卷乘,每次计算$T$中的两个块与$P$的全部.
应用:字符串模糊匹配
定义:字符串的汉明距离
字符串$A,B\in \Sigma^{n}$,字符串的汉明距离定义为$|\{j:a_j \neq b_j\}|$,即两个字符串之间有多少个对应位置不相等.
给定模式串$P=p_1\cdots p_m \in \Sigma^*$,文本串$T=t_1\cdots t_{n}\in \Sigma^*$和限制$k$,判断是否存在$i$,使得$p_1\cdots p_m, t_{i+1}\cdots t_{i+m}$的汉明距离$\leq k$
朴素算法:
暴力,$O(mn)$
算法1:
定义函数序列$\mu_i=|\{j:p_j=t_{i+j}\}|$,计算这个函数
如何计算这个函数呢?对于所有的$c\in \Sigma$,定义
$\mu_i^{(c)}=|\{j:p_j=t_{i+j}=c\}|=\Sigma_j[p_j=c][t_{i+j}=c]$
用$O(n+m)$时间构造出$A^{(c)}=<a^{(c)}_1,\cdots , a^{(c)}_m>,B^{(c)}=<b^{(c)}_1,\cdots,b^{(c)}_n>,a^{(c)}_k=[p_{m+1-k}=c],b^{(c)}_k=[t_k=c]$,后,进行卷积即可.
计算完全部$\mu_i^{(c)}$之后,令$\mu_i=\Sigma_{c\in \Sigma}\mu_i^{(c)}$,然后遍历$\mu_i$,判断其中是否有连续$m$个元素之和$\geq m-k$.
总时间复杂度$O(|\Sigma|n \log n)$
但当$|\Sigma|$较大时又该怎么办呢?
Abrahamson算法(1987):
思路:将字母表中元素按出现次数高低进行分类
记
$\Sigma_H=\{c\in \Sigma : c \text{在模式串$P$中出现次数} > \Delta\}$
$\Sigma_L=\{c\in \Sigma : c \text{在模式串$P$中出现次数} \leq \Delta\}$
这样便保证了$|\Sigma_H|\leq m/\Delta$
1.高频情况
对于每个$c\in\Sigma_H$,使用卷积计算$\mu_i^{(c)}$,并令$\mu_i=\Sigma_{c\in \Sigma_H}\mu_i^{(c)}$,时间复杂度$O(|\Sigma_H|n\log n)=O(\frac{m}{\Delta}n \log n)$
2.低频情况
对于所有$l \in [1:n]$,若$t_l \in\Sigma_L$,那么对于所有满足$p_j=t_l$的$j$(总数$\leq \Delta$),令$\mu_{l-j}$加一.时间复杂度为$O(\Delta n)$
总运行时间$O(\frac{m}{\Delta} n \log n + \Delta n)$,令$\Delta=\sqrt{m\log n}$,总时间复杂度则为$O(n\sqrt{m\log n}+n \log n)= \widetilde{O}(n \sqrt{m})$
注:$\widetilde{O}(\cdot)$指相比$O(\cdot)$多出对数级.
Amir-Lewenstein-Porat(2004) $\widetilde{O}(n \sqrt{k})$
Gawrychowski-Vzanski(2018) $\widetilde{O}(n + \frac{nk}{\sqrt{m}})$
$\vdots$
接近于$O(n)$
应用:子集和问题
对于集合$A$,记$\Sigma(A)$为$A$中所有元素之和.
给定由$S$个正整数组成的集合$S$,目标值$T$,判断是否存在子集$R \subseteq S$使得$\Sigma(R)=T$
暴力枚举+尺取:
将$S$划分为两个大小为$n/2$的子集,枚举全部的子集和并归并排序,然后对以此法获得的两个长度为$O(2^{n/2})$的有序序列进行尺取,时间复杂度$O(2^{n/2})$
DP(动态规划):
时间复杂度$O(nT)$
一个用于求解可多次选取的子集和问题的思路:
观察:如果$S$中的元素可多次选取进$R$中,那么定义
$C^{(j)}[i]=\text{真,当且仅当存在多重集合}R\subseteq S, |R| \leq j,\Sigma(R)=i$
那么$c^{(j)}[i]$可以通过如下公式求出:
$C^{(j)}[i]=\vee_{i'=0}^{i}(C^{(j/2)}[i']\wedge C^{(j/2)}[i-i'])$
这又是一个卷积的形式,以此方法对原问题进行分治,总时间复杂度为$O(T\cdot\log T\cdot \log n)=\widetilde{O}(T)$(假定$n\leq T$,否则集合$S$中要么存在着比$T$还大的元素,可以一开始就排除掉,要么存在着重复元素,要么二者兼有)
但此方法对于一般的(不允许重复选取元素的)子集和问题无效
Bringmann算法(2017):
随机化地用于求解一般子集和问题的算法
引理1:
假定待求集合$S\subseteq [U]$中存在解$R\subseteq S$,且$|R|\leq k$,则存在一个随机化算法,可在$\widetilde{O}(k^2U)$时间内求得这个解.
证明:
记$\begin{pmatrix}a\\b\end{pmatrix}$为组合数,即在$a$个互不相同的物品中取出$b$个的方法数.
考虑如下事实:将$k$个球随机地放进$k^2$个桶中,每个桶中球的个数均$\leq 1$的概率$\geq 1/2$.("存在两个球被放进了同一个桶中"的概率 $\leq \begin{pmatrix}k\\2\end{pmatrix} \cdot \frac{1}{k^2}=\frac{1}{2}$)
那么,考虑将$S$随机地划分为$k^2$个子集,$S=S_{1}\cup S_{2} \cup \cdots \cup S_{k^2}$.
对于给定的$S_x,\cdots,S_{y}$定义$C_{S_x,\cdots,S_y}[i]$为真,当且仅当存在着子集$A$,$\Sigma(A)=i$,且$\forall j \in [x:y],|A \cup S_j| \leq 1$.
那么该序列可以通过如下方式求得:
$C_{S_1,\cdots,S_l}[i]=\vee_{i'=0}^{lU}(C_{S_1,\cdots,S_{l/2}}[i'] \wedge C_{S_{l/2+1},\cdots,S_l}[i-i'] )$
这是一个对长度为$O(lU)$的序列的卷积.用该卷积对$S_1,\cdots,S_{k^2}$进行分治.记求解子集个数为$l$的问题的复杂度为$T(l)$,对复杂度进行分析:
$T(l)=2T(l/2)+O(lU \log (lU))=O((l\log l)U\log(lU))=\widetilde{O}(lU)$
代入$l=k^2$,$T(k^2)=\widetilde{O}(k^2U)$
注意,该方法能否得出正确结果依赖于对于$S=S_1\cup S_2\cup \cdots \cup S_{k^2}$的划分,单次出错概率$\leq 1/2$的情况下可通过重复该算法$\log n$次将出错概率降低至$\leq 1/n$.
引理2:
引理1中的算法可优化至$\widetilde{O}(kU)$
证明:
考虑如下事实:把$k$个球放进$k$个桶里,每个桶中球的个数均$\leq n$个的概率$\geq 1-O(1/n)$(证明参照Chernoff限,待查)
随机地将$S$划分为$k$个子集,$S=S_{1}\cup S_{2} \cup \cdots \cup S_{k}$
定义$C_{S_x,\cdots,S_y}[i]$为真,当且仅当存在着子集$A$,$\Sigma(A)=i$,且$\forall j \in [x:y],|A \cup S_j| \leq \log n$
该序列可以通过如下方式求得:
$C_{S_1,\cdots,S_l}[i]=\vee_{i'=0}^{lU\log n}(C_{S_1,\cdots,S_{l/2}}[i'] \wedge C_{S_{l/2+1},\cdots,S_l}[i-i'] )$
这是一个对长度为$O(lU\log n)$的序列的卷积.
记求解子集个数为$l$的问题的复杂度为$T(l)$,同样分析其复杂度:
$T(1)$是在一个子集$S_j$中,枚举出至多$\log n$个元素所构成的子集和有哪些,这一步可以通过令$S'=S_j,k=\log n$并引入引理1中的算法得到,$T(1)=\widetilde{O}(U)$
$T(l)=2T(l/2)+O(lU\log n \log (lU))$
$T(l)=\widetilde{O}(lU)$
取$l=k$,$T(k)=\widetilde{O}(kU)$
由引理1,2推导出的算法:
对于所有的$u=1,2,4,\cdots,U$,以$S'=\{a_i \in S:a_i \in [u:2u-1]\},k=T/u$为输入,应用引理2中的算法,概率性地计算出$S'=\{a_i \in S:a_i \in [u:2u-1]\}$中的子集和(只保留$\leq T$的那些),单步时间复杂度为$\widetilde{O}(\frac{T}{u}\cdot 2u)=\widetilde{O}(T)$
对于这$O(\log U)$步中计算出的所有子集和进行归并,归并时间为$O(\log U \cdot T \log T)=\widetilde{O}(T)$
Jin&Wu算法(2019):
想法:多项式
求解子集和问题等同于求解$\Pi_{a\in S}(1+x^a) \mod x^{T+1}$,检查$x^{T}$项的系数.
记$exp(x)=e^x$
上式可写成$exp(\Sigma_{a\in S} \ln (1+x^a) ) \mod x^{T+1}$
考虑数学分析知识中的泰勒展开:
$\ln(1+x)=\Sigma_{i=1}^{\infty}\frac{(-1)^i}{i}x^i$
$\ln(1+x^a)=\Sigma_{i=1}^{T/a}\frac{(-1)^i}{i}x^{ai} \mod x^{T+1}$
对于每个$a$,共有$O(T/a)$项需要求和
总运行时间$O(T\Sigma_{a\in S}\frac{1}{a})=O(T \log T)$.
但直接计算这个式子的话是会出现分数项的,考虑随机选取质数$p$,在$Z_p$即模$p$的数域中进行运算,除法运算使用快速幂和费马小定理求模$p$的逆元,这样就变为了一个复杂度为$O(T \log^2 T)$的随机化算法.