Cayley-Hamilton 定理学习笔记

CH 定理主要用于优化线性递推。

下面很多东西都是自己瞎琢磨的,大概错漏挺多。

线代的一些基本知识

感觉学习 CH 困难的很大一部分原因就是缺少一些线代的基础。

  • 矩阵的秩 r(A)<n,说明向量组线性相关,说明行列式 |A|=0。反之,如果 |A|0,那么矩阵满秩。即二者充要。

证明:考虑行列式的性质即可,由于把第 i 行的线性组合加到第 j 行不影响行列式的计算结果,所以如果线性相关,那么说明可以把某一行全部消掉,那么自然不满秩。

  • 对于一个 n 元齐次线性方程组 Ax=0,其中 A 是一个 n×n 的矩阵,x 是一个由未知量构成的长度为 n 的列向量。其存在非全零解的条件是系数行列式 |A|=0

证明:当 |A|0 时,矩阵满秩,感性理解一下,x 就只能是全零了。理性证明的话,考虑把 A 消成上三角矩阵之后,可以解出 xn=0,进一步 xn1 也会解出是 0

扩展

  • 特征值和特征向量

定义:

对于一个 n 阶方阵 A ,若非 0 列向量 vλC 满足 Av=λv

那么称 v 为特征值 λ 的特征向量

理解:

展开式子,有

[公式]

若把矩阵的每一行理解为一个基向量 εi,则是表示基向量与该向量的内积 (εiv)等于 λv

所以我们可以把矩阵 A 视作在 n 维空间中,对 vn 个方向上进行不同程度的拉伸,这个拉伸的过程还可能伴随着旋转,因为如若把 v 视作基,即可以认为是掺杂了其他的基

而这个特征向量特殊的地方就在于,他真的就可以当做是基(即它是空间中的一些特殊的向量,使得 A 对这些向量施加变化的时候只有拉伸作用)

也就是说,矩阵 A 对应的 n 维空间的 n 个方向就是 n 个不同的特征向量所指的方向

此时,这个基构成 n 维空间 S ,设 v 在空间 S 中的表示为 a (投影??)

此时,va 表示的完完全全的一个向量。只是说采用的基底不同

此时,我们计算 Aa ,相当于对 aS 的各个方向上进行了不同程度的拉伸变换(没有旋转了!)。程度的不同体现在 特征值 的不同,特征向量 v 对应的特征值 λ 越大,那么 矩阵 Av 所表示的方向上对 a 的拉伸作用越明显。

具体的,λ>1, 拉伸;λ=1, 不变;λ<1, 压缩

我们知道,向量是不能做除法的,但在特征方向上,就能除了,因为在两个向量共线时,它们所有的对应分量的比值都相同,而不共线时,比值就不同,就没法除了。

或参见:所有方阵都有特征值吗?如何证明? - 天下无难课的回答 - 知乎

  • 矩阵 A 在复数集内有 n 个特征值。

证明:考虑特征值的定义,对于 λ,它是通过一个特征向量来定义的,即二者大概是 1-1 对应,所以只需要证明有 n 个特征向量。

考虑 λx=Ax,等价于 |λEA|x=0,(这是我们熟知的特征多项式的形式)即证明它有 n 个解。

所以 |λEA|=0,这是上方已经证明的结论。

考虑这个行列式,它可以写成 λ 为未知数的一元n次方程组,而 n次方程组在复数集内一共有n个解,(如果我们观察上式,可以发现 λ 只出现在正对角线上,显然A的特征值就是方程组的解)同时,在对应 λ 的条件下,可以解出 x,即特征向量。

这个结论说明了,上方所提到了 “这个基构成 n 维空间 S ”,确实是一个 n 维度的空间。

同时这个其实可以看做是 CH 定理应用逻辑的一部分。

  • 特征多项式

A 是一个 k 阶矩阵,则其特征多项式为 f(x)=|xEA|=xk+c1xk1+c2xk2++ck ,其中 E 是单位矩阵, c 是一系列系数,x 可以取很多种值,如其可以取为一个矩阵或一个数,注意当它取成一个矩阵的时候 f(x) 也是一个矩阵

特征行列式和特征多项式其实指的是一个东西。

  • 两个 n 阶多项式,如果有共同的 n 个零点,那么他们对应项的系数一定相同。

其重点在于,可能两个多项式的主元并不相同,但是系数可以一致。(f(X)f(y) 之类)

证明考虑回忆一下拉格朗日插值的原理之类之类。

  • 特征根方程

在高中阶段,我们学习到这样一种求解线性递推数列的方法:

对于 k 阶线性递推数列 an=i=1kciani,其特征根方程是 xk=i=1kcixki

有这样一个结论,假如对于这个特征根方程有 k 个不同的解,即特征根 λ1,λ2,λk 无重根。

首先an=λin 肯定是上述递推式的解。

其次,由于是线性递推,所以在不考虑初值的情况下,它的几个解的线性组合仍然是它的解。即通解是 an=ikαiλin

然后,只需要带入 a1k 的初值,就可以得到 k 个方程,并解出 α1k,于是就能得到数列 an 的通项公式。

扩展:注意到这里是一定可以解出这 k 个方程的,因为这个关于 α1k 的方程的系数矩阵的行列式写出来是范德蒙德矩阵

这个技巧常常用于秒杀一类高中数列题

再扩展:假如有重根怎么办?留作思考/hsh

  • 线性递推的转移矩阵的特征多项式就是特征根方程

(贺的官老师的)

假设一数列 h 满足 hn(nk)hn=i=1kaihni,转移矩阵 M 的特征多项式为 f(λ)

对于 f(λ)

|λEM|=|[λ1000λ100001akak1ak2λa1]|

Fk(x)=i=0kck,ixi 表示取其最后 kk 列的主子式的特征多项式

考虑计算行列式 ,如若第一行选择 λ ,那么有贡献 λFk1(λ)

如若第一行选择 1 ,容易发现前 k1 行选择的都是 1 ,最后一行会选择 ak ,此时逆序数 k1

所以贡献为 (1)k1·ak(1)k1=ak,即 ck,0=ak

考虑第一种贡献方式,有 ck,i=ck1,i1==cki,0=aki

所以,Fk(x)=xki=0k1akixi ,我们知道 Fk(x)=f(x)

而这个时候,如果我们对比一下 f 和我们的特征根方程,很容易发现他们是一样的!!!

那么进一步的,实际上,如果我们求出了特征多项式,那么我们实际上就获得了线性递推式。(这可能听起来有点鸡肋,我既然已经知道特征多项式,怎么会不知道原递推式呢?但是在下方的例题中展现出了威力)

Cayley-Hamilton定理

下面的等式在方阵 A 满秩时恒成立:

k(λkEA)=0

其中 λkA 的第 k 个特征值。

魔法地,注意到 G(A)=k(λkEA)f(λ)=|λEA| 的零点相同(λ1k),项数也相同,虽然说主元不一样,但是对应的多项式系数就是一样的。(上方基础知识里面讲过)

所以我们可以把上面的定理等价成为 f(A)=O

加速矩阵快速幂

比如,我们要求 MnM 是一个 k×k 的矩阵,平凡的,我们的复杂度是 k3logn ,但是当 k 比较大的时候会被卡掉。这里有一种优化到 O(k3)O(klogklogn) 的方法

具体地,我们可以采用 这篇博客 中的方法先 O(k3) 求出 M 的特征多项式 f(λ)=|λEM|

由于 f(M)=0 ,所以 Mn=Mnmodf(M)

g(x)=xng(x)=h(x)f(x)+r(x) ,由于 f(x)=0,所以 g(x)=r(x)

此时,我们完成了这样的一件事情:将矩阵的幂化成多项式的取模幂

即,设 λnmodf(λ)=i=0k1aiλi ,将 λ=M 带入,需要计算 i=0k1aiMi

所以,我们考虑平时做模意义下整数快速幂的方法,将整数乘法换成多项式乘法,整数取模换成多项式取模,就可以得到 ai ,这部分的复杂度是 暴力乘模 O(k2logn) / NTT O(klogklogn)

最后需要带入 Mi=0k1 ,可以考虑直接 O(k4) 爆算,在齐次线性递推的背景下,就是初值的意思

加速线性递推

我们想要求出 hn(nk)hn=i=1kaihni

设这个转移矩阵 M 的特征多项式为 f(λ)g(λ)=λn

设初值为列向量 B ,第 i 行为 hi1 ,即求 BMn[0]

根据上方讲过的知识,我们可以知道 f(x)=xki=0k1akixi

求解出 f 之后,我们只需要求解 g(x)modf(x) ,得到 (gmodf)(x)=nixi

回到原问题,我们求解的是 BMn[0]=i=0k1niBMi[0] ,又有 BMi[0]=hi(i<k)

好活,这样的话,我们一旦知道 h0k1 那么就可以知道 BMn[0] ,若想要知道 BM 这个列向量的所有值,我们也只需要知道 h02k1

若只求单个元素,总时间复杂度是求 Mn 时的 O(k2logn)/O(klogklogn)

若要求整个列向量,要么直接用 i=0k1niBMi 这个式子暴力矩阵乘法,时间复杂度 O(k2logn+k3),比直接矩阵快速幂少个 log 或者 k,且这样我们就只需要知道 h0,h1,,hk?1

要么挨个元素去算,时间复杂度 O(k3logn)/O(k2logklogn)。可以发现不用 NTTklogk 取模的时间和普通矩快一样,用了的则是把 k 变成了 logk

——gtr


思路来源

多项式取模——听起来一个很奇怪的操作,凭啥会突然想到干这样一件事情呢?

考察我们如何求 fibnfibi=fibi1+fibi2

OI 的方法是,把转移矩阵搞出来,然后搞矩阵 n 次幂啥的。

小学的方法是暴力拆,例如对于 n=5fib5=fib4+fib3=2fib3+fib2=3fib2+2fib1=5fib1+3fib0

考察这个过程,其实就是每次找到等式中最大的一项,然后根据递推式把它变成更小的项。

上述过程,就是通过每次消去最高次项,求 x5 对斐波那契数列的特征多项式 x2x1 取模的结果的过程

特征多项式,也就是特征根方程,每次我们其实是将一个 x2 代换为 x+1,其实也就是 减去 (x2x1) 的若干倍,那么为了代换到底,这个若干倍要尽可能抵到顶,那么实际上做的就是取模。

即,「多项式取模」的过程恰好与「每次按递推式展开下标最大的那项」的过程一致,都可以用「替换」来解读。所以我们直接对 xn 做多项式取模,就可以得到 an=i=1kciai 这类形式。

例 1 「BZOJ4161」Shlw loves matrixI

模版题

#include <bits/stdc++.h>
using namespace std;
const int N = 4444;
const int mod = 1e9 + 7;

char buf[1 << 23], *p1 = buf, *p2 = buf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : * p1 ++)
int read() {
	int s = 0, w = 1; char ch = getchar();
	while(!isdigit(ch)) {
		if(ch == '-') w = -1;
		ch = getchar();
	}
	while(isdigit(ch)) {
		s = s * 10 + (ch ^ 48),
		ch = getchar();
	}
	return s * w;
}  
void inc(int& a, int b) { a = a >= mod - b ? a - mod + b : a + b; }
void dec(int& a, int b) { a = a >= b ? a - b : a + mod - b; }
int add(int a, int b) { return  a >= mod - b ? a - mod + b : a + b; }
int del(int a, int b) { return a >= b ? a - b : a + mod - b; }
int mul(int a, int b) { return 1ll * a * b % mod; }

struct vec {
	int a[N];
	void clear() {
		memset(a, 0, sizeof(a));
	}
}a, b, P, e; 
int n, k, ans; 

vec mul(vec a, vec b, vec P) {
	vec ans; ans.clear();
	for(int i = 0; i <= k; ++ i)
		for(int j = 0; j <= k; ++ j)
			inc(ans.a[i + j], mul(a.a[i], b.a[j]));
	for(int i = k << 1; i >= k; -- i) if(ans.a[i]) 
		for(int j = 0, tmp = ans.a[i]; j <= k; ++ j)
			dec(ans.a[i - j], mul(tmp, P.a[k - j]));
	return ans;
}
vec exp(vec a, int b, vec P) {
	vec ans = e; 
	while(b) {
		if(b & 1) ans = mul(ans, a, P);
		a = mul(a, a, P), b >>= 1;
	}
	return ans;
}

signed main() {
	e.a[0] = 1;
	n = read(), k = read();
	for(int i = 1; i <= k; ++ i)
		a.a[i] = read(), P.a[k - i] = del(0, a.a[i]);
	for(int i = 1; i <= k; ++ i)
		b.a[i - 1] = (read() + mod) % mod;
	
	P.a[k] = 1, a.clear(), a.a[1] = 1;
	a = exp(a, n, P);
	for(int i = 0; i < k; ++ i)
		inc(ans, mul(b.a[i], a.a[i]));
	printf("%d\n", ans);
	return 0;
}

例 2 20240122 序列(sequence)

首先经过一通分析,可以得到这样的 DP 柿子:

fi,j=fi1,j1+(2j+1)fi1,j+(j+1)2fi1,j+1,jk

初值 f0,0=1,求 fn,0

考虑这个咋做。

solution1(std):

如果将 fi 这个向量看做一个整体,那么可以写出一个 k×k 的转移矩阵 M(这也许可以叫做一阶线性递推把)

仔细观察转移式子,可以感性理解到对于每一个 j 而言,Fj(i) 是一个最高 k+1 阶线性递推序列(因为状态之间转移的时候没有对状态本身的平方,即类似 f2 的项,所以都是线性关系)

考虑抽出 F0(i) 来看,而把其他都看成是辅助数组,即把 Fj0(i) 的位置都看成是由 F0 中的某些项线性组合得到的东西。

那么,这个时候,我们的转移矩阵 M 也可以理解是,F0(i) 本身的线性递推转移矩阵 经过非常复杂的线性变化得到的。注意到一个重要性质:特征多项式在基变更下不变。

那么,根据之前的结论,只需要求 M 的特征多项式(根据 Cayley-Hamilton 定理,M 的特征多项式 f(M)=0),就可以得到 F0(i) 的递推公式了。

非常厉害吧!

考察特征多项式:

n+1 阶上述多项式是 dn

若最后一行取右下角,那么 (2n+1x)dn1

若最后一行取 n2,那么倒数第二行只能取 1,有 n2dn2

所以有递推关系 dn=(2n+1x)dn1+n2dn2

其对应的递推矩阵:

[dn,dn1]=[dn1,dn2][2n+1x,1n2,0]

Mn=[2n+1x,1n2,0],那么 dn=[0][0]i=0nMi

现在需要干的事情就是把 i=0kMi 处理出来,其得到的结果矩阵 res 的第一行第一列就是我们要的特征多项式 f

为了避免多项式次数不平衡,这个的方法是考虑分治处理,复杂度 T(k)=2T(k/2)+klogk=klog2k

获得多项式 f 之后,可以直接套 xn 取模的方法解决。但是为了复杂度,需要写一长串的多项式除法之类,非常麻烦,有一种小清新的算法是 波斯坦-茉莉算法

solution2(jsy):

非常 powerful,注意到既然 F0(i) 是线性递推数列,且 Fj0 其实都是 F0 的一种线性组合。那么不妨尝试直接通过递推关系把 F0 解出来。

Fj(x)=i>0fi,jxi,由 DP 式子可以得到:

Fk(x)=xFk1(x)+(2k+1)xFk(x)Fj(x)=xFj1(x)+(2j+1)xFj(x)+(j+1)2xFj+1(x)F0(x)=xF0(x)+xF1(x)+1

Fk 可以表达为 Pk(x)Qk(x)Fk1 的样子。具体的,Fk=x1x(2k+1)Fk1

那么进一步,将 Fi=Pi(x)Qi(x)Fi1 带入 Fi1(x) 的式子里面,可以得到 Pi1Qi1

代入的过程中肯定没有 PQ 相互的乘除,分子分母在代入下一个方程后会变成原来的分子分母的(类似)线性组合,肯定能拿矩阵来转移.(类似于树上主元法的时候的操作过程)

具体的:

[Pi+1,Qi+1][1(2i+1)x,x(i+1)2x,0]=[Pi,Qi]

把中间的矩阵和 solution1 一样分治乘起来,就可以通过 Pk,Qk 得到 P1,Q1

那么这样就可以直接解 F0 了,带进去会是一个求 [xn]P(x)Q(x) 的形式,其中 deg(P)=k,deg(Q)=k+1,完美符合 波斯坦-茉莉 算法的要求,那么其实也可以通过 波-茉 的思路,把实际的递推式搞出来。

波斯坦-茉莉算法

正经的讲解 波斯坦-茉莉算法

时间关系,这里只记录一个关键的关系。

注意到对于给定一个线性递推 ai=j=1dcjaij 之后,对于 Q(x) 的构造是 Q(x)=1i=1dcixi

和特征多项式/特征根方程进行对比,发现多项式 QR 就是特征多项式。

posted @   _Famiglistimo  阅读(393)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-01-25 CF156D Clues 题解
点击右上角即可分享
微信分享提示