逻辑、集合运算上的卷积一览(FMT、FWT,……)
公式渲染修好了。
简介
对于逻辑\(\oplus\)的卷积,而且你不能N方豹草
\]
那么尝试构造变换\(F_{\oplus}\)和反演\(F_{\oplus}^{-1}\)使满足
A_k=F_{\oplus}^{-1}(F_{\oplus}(A))_k
\]
用来加速运算。
或与卷积
或与卷积的变换
定义或、与卷积的变换分别为
\]
如下验证两种变换的可行性
&\begin{aligned}
F_{\vee}(B)_k F_{\vee}(C)_k
&=\sum_{i\vee k=k}B(i)\sum_{j\vee k=k}C(j)
\\&=\sum_{x\vee k=k}\sum_{i\vee j=x}B(i) C(j)
\\&=\sum_{x\vee k=k}A(x)
\\&=F_{\vee}(A)_k
\end{aligned}
&\begin{aligned}
F_{\wedge}(B)_k F_{\wedge}(C)_k
&=\sum_{i\wedge k=k}B(i)\sum_{j\wedge k=k}C(j)
\\&=\sum_{x\wedge k=k}\sum_{i\wedge j=x}B(i) C(j)
\\&=\sum_{x\wedge k=k}A(x)
\\&=F_{\wedge}(A)_k
\end{aligned}
\end{aligned}
\]
验证成功。
如何实现这两种变换?注意到如果将\(n\)位二进制数域映射到一个\(n\)维空间,则\(F_{\vee}(A)_i\)相当于在空间内求高维前缀和,\(F_{\wedge}(A)\)则是求高维后缀和。
因此直接上高维前/后缀和就能做到\(O(n2^n)\)的复杂度,这样的做法属于“快速莫比乌斯变换”。
void FMT_OR(int a[],int len) {
int n=__builtin_ctz(len);
for(int i=0; i<n; ++i)
for(int j=0; j<len; ++j) if((j>>i)&1)
a[j]+=a[j^(1<<i)];
}
void FMT_AND(int a[],int len) {
int n=__builtin_ctz(len);
for(int i=0; i<n; ++i)
for(int j=len-1; ~j; --j) if((j>>i)&1)
a[j^(1<<i)]+=a[j];
}
还有一种通用的方法:“快速沃尔什变换”,复杂度同上。
我们把问题划为n+1个阶段编号0到n,在第i个阶段中,把序列划为\(\frac{2^n}{2^i}\)个区间,并记\(F_{\oplus}(A)_{i,x}\)表示x所在区间中所有下标与x就二进制末i+1位满足特定规则的元素累和。
例如\(F_{\oplus}(A)_{0,x}=A_x\),而所求\(F_{\oplus}(A)_x=F_{\oplus}(A)_{n,x}\)。
从阶段i转移到阶段i+1时,阶段i+1的一个区间内的答案显然由阶段i中位置对应的相邻两个区间内的答案转移而来,此时决策为二进制第i+2末位的取与不取,即从\(F_{\oplus}(A)_{i,l+x}\)和\(F_{\oplus}(A)_{i,l+2^i+x}\)转移到\(F_{\oplus}(A)_{i+1,l+x}\)和\(F_{\oplus}(A)_{i+1,l+2^i+x}\),其中l是阶段i+1中的某个区间的左端点。这四个状态,设为状态A,B,C,D,状态B,D能够表示取到第i+2末位。(其实B的取是假的,因为B不存在在i+2位产生的贡献)。转移按照变换式针对这四个状态做就好了。
例如或卷积中,A,B的下标 或上\(2^{i+1}\)(取到i+2位)得到D的下标,而只有A的下标 或上\(0\)(不取i+2位)得到C的下标;只有B的下标 与上\(2^{i+1}\)(取到i+2位)得到D的下标,A,B的下标 与上\(0\)(不取i+2位)得到C的下标。实现如下
void FWT_OR(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) a[m+i+j]+=a[i+j];
}
void FWT_AND(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) a[i+j]+=a[m+i+j];
}
或与卷积的反演
前/后缀和的反演还能怎么求……
F_{\wedge}^{-1}(A)_k=\sum_{i\wedge k=k} (-1)^{|i|-|k|}F_{\wedge}(A)_k
\]
其中\(|i|\)是将\(i\)的二进制上\(1\)的个数。
先来“快速莫比乌斯反演”做法,直接把变换逆过来做
void IFMT_OR(int a[],int len) {
int n=__builtin_ctz(len);
for(int i=0; i<n; ++i)
for(int j=len-1; ~j; --j) if((j>>i)&1)
a[j]-=a[j^(1<<i)];
}
void IFMT_AND(int a[],int len) {
int n=__builtin_ctz(len);
for(int i=0; i<n; ++i)
for(int j=0; j<len; ++j) if((j>>i)&1)
a[j^(1<<i)]-=a[j];
}
然后是“快速沃尔什反演”做法,步骤与变换类似,只是累和改为消除。
void IFWT_OR(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) a[m+i+j]-=a[i+j];
}
void IFWT_AND(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) a[i+j]-=a[m+i+j];
}
异或卷积
异或卷积的变换
定义异或卷积的变换为
\]
这次不去验证,考虑直接推导【膜rockdu】,首先假定变换\(F_{\veebar}(A)\)与\(A\)线性相关,如下,
\]
当然\(g(,)\)是需要能支持反演的,因此\(g(,)=0\)之类的就不考虑了。那么
F_{\veebar}(A)_k&=\sum_{i}g(k,i)A_i=\sum_{i}g(k,i)\sum_{p\veebar q=i}B_pC_q
\\&=\sum_{i}\sum_{j}g(k,i\veebar j)B_iC_j
\\
F_{\veebar}(B)_k F_{\veebar}(C)_k
&=\sum_{i}g(k,i)B_i\sum_{j}g(k,j)C_j
\\&=\sum_{i}\sum_{j}g(k,i)g(k,j)B_iC_j
\\
g(k,i\veebar j)&=g(k,i) g(k,j)
\end{aligned}
\]
我们需要构造一个\(g(,)\)。
注意\(|i\veebar j|=|i|+|j|\pmod2\),以及\((i\veebar j)\wedge k=(i\wedge k)\veebar (j\wedge k)\),那么
(-1)^{|(i\veebar j)\wedge k|}=(-1)^{|i\wedge k|}(-1)^{|j\wedge k|}
\]
因此令\(g(k,i)=(-1)^{|i\wedge k|}\)就能得到一个合法变换
\]
如何实现这种变换?高维前/后缀和似乎已经G了,使用快速沃尔什变换,相邻两个阶段转移,要讨论对下标与的二进制1的个数的影响,结果如下
F_{\veebar}(A)_{i+1,l+2^i+x}=F_{\veebar}(A)_{i,l+x}-F_{\veebar}(A)_{i,l+2^i+x}
\]
那么变换就完成了
void FWT_XOR(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) {
int x=f[i+j], y=f[m+i+j];
f[i+j]=x+y;
f[m+i+j]=x-y;
}
}
异或卷积的反演
反演时的扣除贡献的式子就是把累和的式子反解,调整后如下
F_{\veebar}^{-1}(A)_{i+1,l+x}=\frac{F_{\veebar}^{-1}(A)_{i,l+x}+F_{\veebar}^{-1}(A)_{i,l+2^i+x}}2\\
F_{\veebar}^{-1}(A)_{i+1,l+2^i+x}=\frac{F_{\veebar}^{-1}(A)_{i,l+x}-F_{\veebar}^{-1}(A)_{i,l+2^i+x}}2\\
\]
实现如下
void FWT_XOR(int a[],int len) {
for(int m=1; m<len; m<<=1)
for(int i=0,s=m<<1; i<len; i+=s)
for(int j=0; j<m; ++j) {
int x=f[i+j], y=f[m+i+j];
f[i+j]=(x+y)/2;
f[m+i+j]=(x-y)/2;
}
}
可以发现所有的/2是可以留到后头算的,即
void IFWT_XOR(int a[],int len) {
FWT_XOR(a,len);
for(int i=0; i<len; ++i) a[i]/=len;
}
混合卷积
子集卷积
要求卷积
\\=\sum_{i\vee k=k}\sum_{j\vee k=k} [|i|+|j|=|k|] B_iC_j
\]
可以枚举补充一维集合大小,从小到大枚举集合大小,分别做一次或卷积,时间复杂度\(O(n^22^n)\)。
其它卷积
还在补。
逻辑、集合运算上的卷积一览(FMT、FWT,……)的更多相关文章
- SQL进阶系列之7用SQL进行集合运算
写在前面 集合论是SQL语言的根基,因为这种特性,SQL也被称为面向集合语言 导入篇:集合运算的几个注意事项 注意事项1:SQL能操作具有重复行的集合(multiset.bag),可以通过可选项ALL ...
- 【Java EE 学习 28 上】【oracle学习第二天】【子查询】【集合运算】【几种数据库对象】
一.子查询 1.为什么要使用子查询:问题不能一步求解或者一个查询不能通过一步查询得到. 2.分类:单行子查询和多行子查询. 3.子查询的本质:一个查询中包含了另外一个或者多个查询. 4.使用子查询的规 ...
- 详解SQL集合运算
以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 ...
- SQL集合运算 差集 并集 交
SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION:交集INTERSECT:差集EXCEPT(在Oracle中叫做 MINUS).在有些数据库中对此的支持不够充分,如MySql中只 ...
- Oracle组函数、多表查询、集合运算、数据库对象(序列、视图、约束、索引、同义词)等
count组函数:(过滤掉空的字段) select count(address),count(*) from b_user max() avg() min(),sum() select sum(age ...
- 【SqlServer系列】集合运算
1 概述 已发布[SqlServer系列]文章如下: [SqlServer系列]SQLSERVER安装教程 [SqlServer系列]数据库三大范式 [SqlServer系列]表单查询 [SqlS ...
- T-SQL基础(四)之集合运算
三个运算符 T-SQL支持三个集合运算符:UNION.INTERSECT.EXCEPT. 集合运算符查询的一般形式如下: Query1 <set_operator> Query2 -- 这 ...
- SQL Server进阶(七)集合运算
概述 为什么使用集合运算: 在集合运算中比联接查询和EXISTS/NOT EXISTS更方便. 并集运算(UNION) 并集:两个集合的并集是一个包含集合A和B中所有元素的集合. 在T-SQL中.UN ...
- EXCEPT(差集)集合运算
在集合论中,集合A与B的差集(A-B)是由属于集合A,但不属于集合B的元素组成的集合.可以认为两个集合的差A-B就是从A中减去B中也属于A的元素. 在T-SQL中,集合之差是用EXCEPT集合运算实现 ...
随机推荐
- Qt on Android 资源文件系统qrc与assets
使用 Qt 为 Android 开发应用时,有时我们的应用会携带一些资源文件,如 png . jpg 等,也可能有一些配置文件,如 xml 等,这些文件放在哪里呢?有两种方式:qrc和assets,咱 ...
- 通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能
原文:通通WPF随笔(1)--基于lucene.NET让ComboBox拥有强大的下拉联想功能 我一直很疑惑百度.谷哥搜索框的下拉联想功能是怎么实现的?是不断地查询数据库吗?其实到现在我也不知道,他们 ...
- IDEA 自动化配置
# IDEA maven web项目:http://www.cnblogs.com/Sinte-Beuve/p/5730553.html # IDEA 数据库自动化 ## 功能 ① SQL 代码自动感 ...
- C# 事件详解
1.事件的本质是什么 答:事件是委托的包装器,就像属性是字段的包装器一样 2.为什么有了委托还有有事件 委托可以被访问就可以被执行,事件则只能在类的内部执行 3.事件要怎么声明 a.明一个委托 //委 ...
- 【Linux】PuTTY----------windows访问Linux 快捷方便
第一步:百度PuTTY,下载好后直接运行,界面如下: 第二步:后输入IP:10.45.XX.XX,直接点击open按钮 第三步:输入用户名: 第四步:密码~ 现在,您就可以对你访问的linux设备进行 ...
- AFN小结(简单的封装)
AFN小结 1,AFN概念.原理 2,AFN的封装使用 3,AFN与其它框架对比 ————————————————————————————————— 1 , AFN的概念原理: AFN的基础是NSUR ...
- WPF应用程序嵌入第三方exe
把其它应用嵌入到C#窗口 源代码-CSDN下载 https://download.csdn.net/download/aiqinghee/10652732 WPF应用程序嵌入第三方exe - gao2 ...
- C#关于多线程的笔记
Thread thNetwork; thNetwork = new Thread(new ThreadStart(GetNetworkInfo));//创建一个线程 thNetwork.Start() ...
- windows 2003 无法安装 .net4.0 windows服务
错误: InstallUtil.InstallLog文件中的错误信息: 正在运行事务处理安装. 正在开始安装的“安装”阶段. 查看日志文件的内容以获得 D:\Debug\DataChang.e ...
- delphi 获取当前进程的cpu占用率
type TProcessCpuUsage = record private FLastUsed, FLastTime: Int64; FCpuCount:Integer; publ ...