线性基

【定义】

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

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

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

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

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

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

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

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

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

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

【性质】

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

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

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

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

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

  3. V 中任意向量可被向量组 a=(a1an) 表示,则 aV 的一组基。

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

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

【求法】

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

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

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

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

【高斯消元】

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

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

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

下面进入正题。

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

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

(不明白的回这里看)

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

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

很巧妙吧?为什么呢?

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

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

【贪心法求解】

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

每加入一个向量 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,甚至可以 220 的搜索)

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

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

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

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

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

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

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


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

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

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

求异或第 k 小:

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

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

posted @   FLY_lai  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示