线性基

【定义】

知识:向量组、线性空间、线性相关性。

  1. 向量组:一组向量,各个维数相等。

  2. 线性表示:如果一个向量能被一个向量组中若干个向量乘上系数后相加得到,称这个向量可被此向量组线性表示。

    (例如向量组 \(s=(v1,v2)\)\(tmp=2v1+3v2\),则 \(tmp\) 可被 \(s\) 线性表示)

  3. 线性无关:设向量组 \(v\),如果 \(v\) 中任意一个向量都不能被 \(v\) 中其他向量线性表示,则称 \(v\) 是线性无关的。

    线性相关:向量组 \(v\) 不线性无关,就线性相关。

  4. 线性空间:记一个向量组 \(v=(v1,v2,\dots,vk)\),任取 \(k\) 个系数 \(a1\sim ak\in \mathbb{R}\),则 \(v\) 的 “生成子空间” 为 \(S=\{a1v1+a2v2+\dots+akvk\}\)

    说人话,用一个向量组能线性表示的所有向量,构成这个向量组的子空间。

而对于一个线性空间 \(V\) 的 “极大,线性无关,的向量组” 为 \(V\) 的线性基,简称基。

可以看出,一组基可以理解为一个线性空间的简化表示。

【性质】

对于一个维度为 \(n\) 的线性空间 \(V\)

  1. \(V\) 中任意 \(n+1\) 个向量构成的向量组必然线性相关。

    很好理解,类比有 \(n+1\) 个方程,必然有一个冗余。

  2. \(V\) 中任意 \(n\) 个线性无关的向量是一组基。

    因为 \(n+1\) 个已经不符合线性无关的要求了,所以 \(n\) 个线性无关已经是极大的了。

  3. \(V\) 中任意向量可被向量组 \(a=(a1\sim an)\) 表示,则 \(a\)\(V\) 的一组基。

    感性理解:\(n\) 维向量,至少需要 \(n\) 个才能单独解开每一维,所以基至少需要 \(n\) 个向量;由上可知不可有 \(n+1\) 个向量。所以 \(n\) 个线性无关的向量必为基

  4. \(V\) 中任意取小于 \(n\) 个向量,一定可以再从 \(V\) 中选足够的向量,一起构成一组基。

【求法】

当给定一个向量组,想要求出它的子空间是很容易的:给每个向量乘一个系数加起来就行。

但是给定一个向量集合,求出它的基,貌似并不容易。

于是就产生了一个问题:给定一个线性空间,如何求出它的基?

(因为线性空间是无穷的,所以通过给出一个生成它的向量组表示)

【高斯消元】

考虑一下,线性基的本质就是去除了所有冗余向量。而这和消元有异曲同工之妙,我们可以使用高斯消元法来解。

要先保证高斯消元的操作:交换两行和消元是不会导致信息损失的,换而言之,交换两个向量的位置,和两个向量求和/差再替换,不会使线性空间变化。

而这是显然的,向量加减不会使线性空间变化,交换更不会。

下面进入正题。

先把向量组写成一个矩阵的形式,每一行代表一个向量,行数是向量组的大小。

对这个矩阵进行高斯消元,得到一个上三角矩阵:从左上角开始的一条斜对角线上都是 \(1\),其他的都是 \(0\)。(其实本来对角线上方的可以不是 \(0\),但是通过消元也可以除了对角线全部改成 \(0\)

(不明白的回这里看)

其实这条对角线上有可能是 \(0\)。但是如果是 \(0\),代表所有向量这一维都是 \(0\),那根本就没有存在的必要。如果不是 \(1\),可以用除法。

现在得到了一个上三角矩阵,这个矩阵的前 \(n\) 行就是一组基。

很巧妙吧?为什么呢?

设前 \(n\) 行的向量集合为 \(S\)。考虑 \(S\) 中任意多个向量 \(v1\sim vx\),不妨让它们按照在矩阵中行数大小从小到大排序。

此时 \(v1\) 在矩阵中是最靠上的,按照上三角矩阵的定义,有一维 \(v1\)\(1\)\(v2\sim vx\) 都是 \(0\)。那 \(v1\) 必不可能通过 \(v2\sim vx\) 线性表示,证毕。

【贪心法求解】

初始线性基为空,考虑在不断加入新元素的过程中维护线性基。

每加入一个向量 \(vec\),枚举每一维。我们只需要判断 \(vec\) 是否存在无法被表示出来。

这里异或线性基和实数线性基有一点点不同。但都需要额外开一个数组 \(b\),初始 \(b\) 为空。

  1. 异或线性基。每加入一个数 \(x\),将其二进制分解从高位到低位扫。如果第 \(p\) 位是 \(1\),看一下 \(b[p]\) 是否存在:如果是,让 \(x\) 整个数异或上 \(b[p]\);否则,令 \(b[p]=x\)

    这个过程感性理解,每找到一个数,就用之前确定的线性基尽可能从高到低消位。实在消不了了,才把消完的加入。

    因为消完的前面一定是连续若干个位被线性基消了,而且至少消掉了原来(线性基个数)个位,所以这么做一直是线性无关的。

    void insert(unsigned long long x) {
     	for (int i = 63; ~i; --i) {
     		if (!(x >> i))
       			continue;
     		if (!b[i]) {
       			b[i] = x;
       			break;
     		}
     		x ^= b[i];
     	}
     }
    
  2. 实数线性基。\(b\) 数组要记录高斯消元的对角线元素

    每加入一个向量 \(v\),循环它的每一维。如果这一维 \(b\) 上不是零,用贡献了这一维 \(b\) 的向量把 \(v\) 的这一维消成零(注意同时 \(v\) 这一维后面的维度也可能被一起消掉),类似高斯消元。如果 \(b\) 这一维是 \(0\),把现在剩下的 \(v\) 加入线性基。

    for (int i = 1; i <= n; ++i) { //插入 v[i]
        for (int j = 1; j <= m; ++j) {
            if (v[i].a[j] == 0)
                continue;
            if (b[j] == 0) { //当前线性基消不了
                b[j] = i; //把剩下的v[i]加入
                break;
            }
            double mul = v[i].a[j] / v[b[j]].a[j];
            for (int k = j; k <= m; ++k)
                v[i].a[k] -= mul * v[b[j]].a[k];
            //类高斯消元
        }
    }    
    

【复杂度】

\(O(nm)\)\(n\) 为向量维数,\(m\) 为初始向量组大小。

【用途】

求出这个玩意,有什么用?

基很重要的一个性质,就是它能表示整个线性空间。而且它只有维度 \(n\) 个向量。

一般初始给出的向量组会很多(一般 \(1e5,5e5,1e6\)),但一般维度比较少(不超过 \(100\))。如果我们求出了线性基,就可以大大简化。(如果维度只有 \(20\),甚至可以 \(2^{20}\) 的搜索)

简化了向量组,无论是什么操作,都会好搞很多。

一个经典的例题:给定一些数,选若干个使异或和最大。

首先需要明确的一点是:异或可以看作不进位加法,它同时具有交换律和结合律,所以才能使用高斯消元搞线性基。不是什么向量运算都能搞线性基的。

把每一个数看作二进制数,每一位单独看作向量的一个维度,则题目可以看作 \(1e5\)\(50\) 维向量。

对这个向量求线性基。最后会剩下不到 \(50\)\(50\) 维向量。我们只需要用不到 \(50\) 个数就能表示出原来 \(1e5\) 个数表示的集合!!!

求完线性基,现在就是求异或最大值了。由于 \(2^t>2^{t-1}+\dots+1\) 的特殊性,一种想法是按照二进制最高位从大到小枚举,如果异或了可以让这一位更大就异或。

但是有上面的性质:除了对角线,其他都是 \(0\),(对角线可能为 \(0\) 但是不重要)直接把线性基全部异或起来,就是异或最大值了。


同时线性基还有一些同样经典的操作:

判断一个数是否能被异或出来:

求完线性基后把这个数做类似贪心法求解过程中的插入,如果这个数最后被消成 \(0\) 说明可以凑出来,否则说明不行。

求异或第 \(k\) 小:

求出线性基后,从高到低枚举每个线性基。因为线性基一定是对角线都是 \(1\),其余都是 \(0\),所以如果当前有 \(cnt\) 个元素,就有 \(2^{cnt}\) 种不同的异或值。

当我们枚举到第 \(p\) 个元素,判断一下 \(k\) 是否大于 \(2^{n-p}\),如果是,让 \(k-=2^{n-p}\),同时选择异或上这个数;如果不是,不异或这个数。枚举完所有元素,就得到了异或第 \(k\) 小。(有点像对 \(k\) 二进制拆位,如果这一位有就异或对应的数)

posted @ 2024-02-01 09:41  FLY_lai  阅读(17)  评论(0编辑  收藏  举报