【杂谈】01矩阵乘法
前言
前几天看了一下 201720172017 年论文《非常规大小分块算法初探》,里面有一节讲到了 010101 矩阵乘法,介绍了一种 O(n3ωlogn)\mathcal O({n^3\over\omega\sqrt{\log n}})O(ωlognn3) 的分块做法。然而其代码实现难度高,复杂度优化效果小,我曾向 Rainybunny\sf RainybunnyRainybunny 说,不会真的有人写这玩意儿骗分吧!
结果我还是写了分块 010101 矩阵乘法来骗分。但是并不是上面的方法。是我无意中想到的一个分块方法。为什么要写出来呢?可能也是为了留作纪念吧……
概述
这里的 010101 矩阵是,在模 222 意义下(在域 GF(2)\Bbb{GF}(2)GF(2) 内)进行所有操作,包括答案矩阵也要对 222 取模。
二维分块
这是论文中的方法,先简单讲讲。不妨设要计算 A×BA\times BA×B,将两个矩形都划分成若干 S×SS\times SS×S 大小的子矩形。花费 O(4S2S3ω)\mathcal O(4^{S^2}{S^3\over \omega})O(4S2ωS3) 的时间,预处理每一种不同的情况对应的结果。然后用 O[(nS)3S2ω]=O(n3Sω)\mathcal O[({n\over S})^3{S^2\over\omega}]=\mathcal O({n^3\over S\omega})O[(Sn)3ωS2]=O(Sωn3) 的复杂度合并结果。取 S=log4n2S=\sqrt{{\log_4 n}\over 2}S=2log4n 时,有最优复杂度 O(n3ωlogn)\mathcal O({n^3\over{\omega \sqrt{\log n}}})O(ωlognn3) 。
一维分块
为什么上面的方法不优呢?因为 S⩽ωS\leqslant\omegaS⩽ω,所以 bitset\tt bitsetbitset 的效用发挥不出来了。bitset\tt bitsetbitset 才是最强的分块者!
考虑 ω\omegaω 和 SSS 的分块效果同时存在。回到原式 Ci,j=∑kAi,kBk,jC_{i,j}=\sum_k A_{i,k}B_{k,j}Ci,j=∑kAi,kBk,j 上,不妨设 iii 是未分块的一维。能不能对 jjj 做 SSS 分块呢?相当于矩阵 AAA 的某个行向量,要点乘 SSS 个列向量。似乎并没有冗余计算。估计行不通。
所以考虑对 kkk 做 SSS 分块。也就是 AAA 的行向量的一部分,长度仅为 SSS,代表 BBB 的某些行向量是否被选择,然后异或到第 iii 行上。长度仅为 SSS,这提示我们 Method of four Russians\text{Method of four Russians}Method of four Russians,即讨论 2S2^S2S 种不同的 Ai,k (k∈[l,r])A_{i,k}\;(k\in[l,r])Ai,k(k∈[l,r]) 的情况,并直接计算出对第 iii 行的贡献。
那么 BBB 中每 SSS 行就要暴力计算一次,这是 O(nS⋅2S⋅nω)\mathcal O({n\over S}\cdot 2^S\cdot{n\over\omega})O(Sn⋅2S⋅ωn) 的,而 AAA 中只需要 O(n2S)\mathcal O({n^2\over S})O(Sn2) 次行向量操作,是 O(n3ωS)\mathcal O({n^3\over \omega S})O(ωSn3) 的,取 S=lognS=\log nS=logn 有最优复杂度 O(n3ωlogn)\mathcal O({n^3\over \omega\log n})O(ωlognn3),且代码实现极其简单。
代码实现
nnn 是矩阵大小,stepstepstep 是块的大小,已在主函数内求出。
const int MAXN = 2000;
const int LOGN = 10;
bitset<MAXN> dp[1<<LOGN];
int lowbit[1<<LOGN], n, step;
struct Matrix{
bitset<MAXN> a[MAXN];
inline void clear(){
for(int i=0; i!=n; ++i)
a[i].reset();
}
Matrix operator * (const Matrix &b) const {
Matrix c; c.clear();
for(int l=0,r=step; l!=n; l=r,r+=step){
if(__builtin_expect(r > n, false))
r = n; // not to exceed n
for(int S=1; S!=(1<<step); ++S){
const int &id = lowbit[S];
dp[S] = dp[S^(1<<id)]^b.a[l+id];
}
for(int i=0,now=0; i!=n; ++i,now=0){
for(int j=l; j!=r; ++j)
now ^= (a[i].test(j))<<(j-l);
c.a[i] ^= dp[now]; // find in table
}
}
return c;
}
};
更多推荐


所有评论(0)