基础数论知识

前言

基础数论知识。

original edition 2023.3.29。

upd 2023.6.19:为明天听 zyw 大佬讲题复习,并优化 Latex。

upd 2023.6.20:扩展欧几里得,同余最短路,逆元,中国剩余定理及扩展。

upd 2023.6.21:BSGS。

upd 7.2:高斯消元。

upd 10.20: csp-s 考前复习,更正逆元板块一些错误。

知识点

线性筛

  1. 原理:让每个数被它最小质因数筛一次。
  2. \(i\bmod prime[j]=0\),则 \(i\cdot prime[j+k],k\geq0\) 的最小质因数为 \(prime[j]\) 而非 \(prime[j+k]\)。此时筛完 break。

代码实现:

for(int i=2;i<=n;i++)
{
	if(!v[i]) p[++k]=i;
	for(int j=1;j<=k&&i*p[j]<=n;j++)
  	{
  		v[i*p[j]]=1;
	  	if(i%p[j]==0) break;
	}
}

唯一分解定理

  1. \(N=p_{1}^{c_{1}}p_{2}^{c_{2}}...p_{m}^{c_{m}}\)
  2. \(N\) 正约数个数:\(\prod_{i=1}^{m}(c_{i}+1)\)
  3. \(N\) 正约数和: \(\prod_{i=1}^{m}(\sum_{j=0}^{c_{i}}(p_{i})^j)\)

最大公约数

  1. \(a\leq b,\gcd(a,b)=\gcd(b,b-a)=\gcd(a,b-a)\)
  2. \(b\neq 0,\gcd(a,b)=\gcd(b,a\bmod b)\)

证明:

  • \(a<b,\gcd(b,a\bmod b)=\gcd(b,a)\)
  • \(a\geq b\),设 $a=q\cdot b+r,0\leq r<b,r=a\bmod b $
  • \(d\mid a,d\mid b\)\(d\)\(a,b\) 公约数集合任一数。则 \(d\mid (a-qb)\)\(d\mid r\)。所以 \(b,a\bmod b\) >和 \(a,b\) 有相同公约数集合,故最大公约数相同。

tip : 设 \(a=k_{1}d,b=k_{2}d\),则 \((a-qb)=(k_{1}-k_{2}q)d\),必然整除 \(d\)

  1. \(\gcd(a,b) \cdot \operatorname{lcm}(a,b)=a \cdot b\)

欧拉函数

  1. \(\varphi(n)\) 定义:$1\sim N $ 中与 \(N\) 互质的数的个数。
  2. 根据唯一分解定理 \(N=p_{1}^{c_{1}}p_{2}^{c_{2}}...p_{m}^{c_{m}}\)\(\varphi(N)=N\times \prod_{i=1}^{m}(1-\frac{1}{p_{i}})\)

证明:

对于每个质因子 \(p\)\(1\sim N\) 中均有 \(\frac{N}{p}\) 个倍数。

对于每几个质因子(这里举两个) \(p_{i},p_{j}\)\(p_{i}p_{j}\) 的倍数被减了两次,需要加回来一次,即 \(N-\frac{N}>{p_{i}}-\frac{N}{p_{j}}+\frac{N}{p_{i}p_{j}}=N(1-\frac{1}{p_{i}})(1-\frac{1}{p_{j}})\)(容斥原理)


  1. 欧拉函数性质(省略版)

(1). \(\forall n>1\)\(1\sim n\) 中与 \(n\) 互质的数的和为 \(n\cdot \varphi(n)/2\)

(2). 欧拉函数是积性函数。积性函数性质:\(f(ab)=f(a)\cdot f(b)\)\(a,b\)互质。

(3). 若 \(p\) 是质数,\(p\mid n,p^2\mid n\),则 \(\varphi(n)=\varphi(n/p)\cdot p\)

(4). 若 \(p\) 是质数,\(p\mid n,p^2\nmid n\),则 \(\varphi(n)=\varphi(n/p)\cdot (p-1)\)

tip:\(\varphi(p)=p-1\)\(p\) 为质数(除了 \(p\) 的倍数,肯定 \(1\sim p-1\) 都与 \(p\) 互质)。

证明:

(1). 设 \(x\)\(n\) 互质,又 \(\gcd(n,x)=\gcd(n,n-x)=1\) 所以与 \(n\) 互质的数 \(x,n-x\) 成对出现,平均值为 \(n/>2\),有 \(\varphi(n)\) 个。

(2). 请自行用公式计算证明。

(3). 设 \(n=kp^2\),则 \(n/p=kp\),显然 \(n,n/p\) 质因子相同,代入公式算即可。

(4). \(p^2 \nmid n\)\(p \nmid n/p\),故 \(n/p\) 不是 \(p\) 倍数,\(p\) 又为质数,二者肯定互质,利用积性函数性计算即可。

tip:根据(3),(4),可以线性求欧拉函数(很简单,直接手推一波啥事没有)。

欧拉定理及扩展

  1. 若正整数 \(a,n\) 互质,则 \(a^{\varphi(n)}\equiv 1\pmod n\)。(\(n\) 为质数你猜猜是啥)
  2. 若正整数 \(a,n\) 互质,则对于任意正整数 \(b\),有 \(a^b \equiv a^{b \bmod \varphi(n)} \pmod n\)
  3. \(a,n\) 不一定互质,且 \(b>\varphi(n)\),有 \(a^b\equiv a^{b \bmod \varphi(n)+\varphi(n)} \pmod n\)

tip : 是费马小定理。


证明:

(1)先引入两个概念,同余类和剩余系。

对于 \(\forall a \in [0,m-1]\),集合 \(\{a+km\}\)\(k\) 为整数)的所有数模 \(m\) 余数都是 \(a\),则称该集合为一个模 \(m\) 的同余类,简记为 \(\overline{a}\)

\(m\) 的完全剩余系:一共有 \(m\) 个,分别为 \(\overline{0},\overline{1},\overline{2},...,\overline{m-1}\)

\(m\) 的简化剩余系:一共有 \(\varphi(m)\) 个,即 \(1\sim m\) 中与 \(m\) 互质的个数。

例如:模 \(10\) 的简化剩余系为 \(\{\overline{1},\overline{3},\overline{7},\overline{9} \}\)

此外,简化剩余系关于模 \(m\) 乘法封闭(集合内两个数相乘,还是在集合内)。

因为 \(a,b\)\(m\) 互质,则 \(a\times b\) 也与 \(m\) 互质,\(a\times b \bmod m\) 也与 \(m\) 互质。

(2) 接下来证明欧拉定理。

\(n\) 的简化剩余系为 \(\{\overline{a_{1}},\overline{a_{2}},...,\overline{a_{\varphi(n)}} \}\)

\(\{\overline{aa_{1}},\overline{aa_{2}},...,\overline{aa_{\varphi(n)}} \}\) 也可表示为 \(n\) 的简化剩余系。(A)

证明(A):

由于 \(a,n\)互质和简化剩余系的乘法封闭,故 \(\overline{aa_{i}}\) 也在简化剩余系集合中。我们假设 \(aa_{i}\equiv aa_{j} \pmod n,\ i\neq j\),根据同余性质,两边同时除以 \(a\) 仍然成立,再根据 \(a_{i},a_{j}<n\),故 \(a_{i} = a_{j}\)。在简化剩余系中,显然不存在 \(a_{i}=a_{j}\),故原结论成立。

证毕 (A)

综上: \(a^{\varphi(n)}a_{1}a_{2}...a_{\varphi(n)}\equiv (aa_{1})(aa_{2})...(aa_{\varphi(n)})\equiv a_{1}a_{2}...a_{\varphi(n)}\pmod n\)

因此 \(a^{\varphi(n)} \equiv 1\pmod n\)


证明:

\(b=q\cdot \varphi(n)+r,0\leq r < \varphi(n)\)

\(a^b \equiv a^{q\cdot \varphi(n)+r}\equiv (a^{\varphi(n)})^q\cdot a^r \equiv 1\cdot a^r \equiv a^{b\bmod \varphi(n)}\pmod n\)

tip :注意第一条定理,和 \(r\) 等于什么


证明:

先引入一个结论:若 \(n_{1},n_{2}\) 互质,\(x\equiv y\pmod {n_1},x\equiv y \pmod {n_2}\),则\(x \equiv y \pmod {n_1 n_2}\)

这个移项后发现 \(x-y\)\(\operatorname{lcm}(n_{1},n_{2})\) 的倍数,然后 \(\operatorname{lcm}(n_{1},n_{2})=n_{1}n_{2}\),故成立。

受这个启发,我们先把 \(n\) 质因数分解为 \(n=p_{1}^{c_{1}}p_{2}^{c_{2}}...p_{m}^{c_{m}}\)。显然,任两个 \(p_{i}^{c_{i}}\) 互质。如此,我们只需要证明 \(\ a^b\equiv a^{b \bmod \varphi(n)+\varphi(n)} \pmod {p_i^{c_i}}\)
,再把它们乘起来,原式得证。

接下来,我们分析如何证上面的式子。

\(\gcd(a,p_{i}^{c_{i}})=1\),显然符合欧拉定理 1,2 条结论,故可以证明,这里不多赘述。

\(\gcd(a,p_{i}^{c_{i}})\neq 1\),则可设 \(a=k\cdot p_{i}\)。(显然,\(a\)\(p_{i}\) 的倍数,毕竟 \(p_{i}\) 是质数嘛)

由于 \(\varphi(p_{i}^{c_{i}})=p_{i}^{c_{i}} - \frac{p_{i}^{c_{i}}}{p_{i}}=p_{i}^{c_{i-1}}\cdot (p_{i}-1)\geq p_{i}^{c_{i-1}} \geq 2^{c_{i}-1} \geq c_{i}\)

所以 \(b\geq \varphi(n) \geq \varphi(p_{i}^{c_{i}}) \geq c_{i}\)

因此 \(p_{i}^{c_{i}}\)\(\ a^b\)\(\ a^{b\bmod \varphi(n)+\varphi(n)}\) 的因数,余数均为 \(0\),故得证。

tip 1:关于 \(\varphi(p_{i}^{c_{i}})\) 的求法,我们考虑只有 \(p_{i}\) 的倍数和 \(p_{i}^{c_{i}}\) 不互质,减去即可。

tip 2:不等式里"忽视" \(p(i-1)\) 和把 \(p_{i}\) 放缩为 \(2\) 的理由:\(p\) 是质数,大于等于 2。

tip 3:如果不理解因数,注意 \(a=k\cdot p_{i}\),结合不等式,次方后必定是包含 \(p_{i}^{c_{i}}\) 的。


扩展欧几里得

  1. 裴蜀定理:对于任意整数 \(a,b\),存在一对整数 \(x,y\),满足 \(ax+by=gcd(a,b)\)

证明:

  • 根据 \(\gcd(a,b)=\gcd(b,a \bmod b)\),在最后 \(b=0\) 时,显然有 \(x=1,y=0\) 满足 \(a\cdot 1+b\cdot 0=gcd(a,0)\)
  • 设当前 \(ax'+by'=\gcd(a,b)=bx+(a \bmod b)y=bx+(a-b\lfloor a/b \rfloor)y=ay+b(x-\lfloor a/b \rfloor y)\)
  • \(x'=y,y'=x-\lfloor a/b \rfloor y\)。如此递归便能构造。
  1. 此过程还算了上述方程的一组特解,该算法为扩展欧几里得算法。代码实现:
void exgcd(int a,int b,int &x,int &y,int &d)
{
    if(b==0) return x=1,y=0,d=a,void();
    exgcd(b,a%b,x,y,d);
    int t=x;x=y,y=t-a/b*y;
}
  1. \(ax+by=c\) 的整数解。
  • 先判是否有解。设 \(d=\gcd(a,b)\),有解当且仅当 \(d\mid c\)
  • 特解:用 exgcd 求出方程 \(ax+by=d\) 的特解 \(x_0,y_0\),再同时乘上 \(c/d\),就得到原方程的特解 \(x_1,y_1\)
  • 通解:\(x=x_1+\frac{b}{d}k,y=y_1-k\frac{a}{d},k\in \mathbb{Z}\)

过程:假设将 \(x_1\) 扩大为 \(x_1+p\)观察法发现 \(y_1\) 需要减小,设减小为 \(y_1-q\)。(\(p,q \in \mathbb{N}^+\),事实上是我们希望 \(p,q\) 为正整数)

\(\begin{cases}ax_1+by_1=c \\ a(x1+p)+b(y_1-q)=c \end{cases}\)

得:\(p=\frac{bq}{a},q=\frac{ap}{b}\),又 \(p,q \in \mathbb{N}^+\),所以 \(a \mid bq,b \mid ap\)

\(p,q\) 最小(也就是最小偏差量),就可以表示通解了。不难得出 \(bq_{min}=\operatorname{lcm}(a,b)=\frac{ab}{d}\)\(p\) 同理,得 \(p_{min}=\frac{b}{d},q_{min}=\frac{a}{d}\)

  1. 求解线性同余方程 \(ax \equiv b \pmod n\)

移项得 \(ax-b \equiv 0 \pmod n\),即 \(ax-b\) 使 \(n\) 的倍数。假设为 \(-y\) 倍,则 \(ax+ny=b\)。就是刚刚的不定方程。

同余数最短路

一般用来解决用若干整数使用任意次拼数这类的问题。

显然可以用完全背包来解决,复杂度 \(O(nV)\)\(V\) 为背包体积。

若用 dijkstra 求解最短路,设这若干整数绝对值最小的为 \(m\),复杂度为 \(O(n\log (n+m))\)

假设我们用 \(a_1,a_2,...,a_n\) 拼数。

选定 \(a_1\) (可以选任意数)为模数。考虑 \(a_1\) 的同余类 \(\overline{x}\),若其中一个数 \(t\) 能被 \(a_2,a_3,...,a_n\) 凑出,则 \(\overline{x}\) 中大于等于 \(t\) 的数都能被凑出。

由此,考虑 \(a_1\) 个同余类中最小能被凑出的数,设为 \(d\),则此同余类在 \([0,r]\) 中能凑出的数的个数为 \(\lfloor \frac{r-d}{a_1} \rfloor\)

问题转化为如何求每个同余类最小能被凑出的数。

这可以转化为一个图论问题。对每个同余类建一个节点,向其加 \(a_i,i\in[2,n]\) 到达的同余类分别连边,边权为 \(a_i\)。那么问题就转化为了跑最短路。

代码实现:

for(int i=0;i<a[1];i++)
	for(int j=2;j<=n;j++)
		G[i].emplace_back((i+a[j])%a[1],a[j]);
dijkstra();// spfa or other algorithms
// 假设要求统计能凑出 [l,r] 中多少不同的数。
ll ans=0;
for(int i=0;i<a[1];i++)
{
	if(r>=d[i]) ans+=(r-d[i])/a[1]+1;
	if(l-1>=d[i]) ans-=(l-1-d[i])/a[1]+1;
}

逆元

实用主义定义:考虑一个分数 \(\frac{a}{b}\)\(p\) 如何进行取模运算。就是乘上 \(b\) 关于 \(p\) 的逆元。

  1. \(O(\log p)\) 求逆元:
  • 设逆元为 \(x\),根据定义,\(\frac{a}{b} \equiv ax\)。 即 \(x \cdot b \equiv 1 \pmod p\)
  • \(p\) 为质数,根据费马小定理 \(b^{p-1} \equiv 1 \pmod p\),得 \(x=b^{p-2}\)
  • \(b,p\) 互质,则用 exgcd 求解上述线性同余方程(所以逆元可能不存在)。
  1. 线性求逆元:
  • 设当前要求 \(i\) 的逆元,\(p=q \cdot i+r\),即 \(q \cdot i+r \equiv 0 \pmod p\)
  • 同时乘 \(i^{-1},r^{-1}\)\(q \cdot r^{-1}+i^{-1} \equiv 0 \pmod p\)
  • \(i^{-1} \equiv -q \cdot r^{-1} \pmod p\)
  • 为了方便,让逆元非负,右边加上 \(p\cdot r^{-1}\)(此数为 \(p\) 的倍数,模 \(p\) 意义下显然允许),得最终式子:
  • \(i^{-1} \equiv (p-p/i) \cdot (p \bmod i)^{-1}\)
inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
  1. 线性求阶乘逆元:
  • \((n!)^{-1} \equiv ((n+1)!)^{-1} \cdot (n+1) \pmod p\)
inv[n]=qpow(fac[n],p-2);
for(int i=n-1;~i;i--) inv[i]=1ll*inv[i+1]*(i+1)%p; 

中国剩余定理及扩展

\(p_1,p_2,...,p_n\) 是两两互质的整数,\(p=\prod\limits_{i=1}^{n}p_i,P_i=p/p_i\)\(t_i\) 是线性同余方程 \(P_it_i \equiv 1 \pmod {p_i}\) 的一个解,则对于方程组

\(\begin{cases} x \equiv a_1 \pmod {p_1} \\ x \equiv a_2 \pmod {p_2} \\...\\ x \equiv a_n \pmod {p_n}\end{cases}\)

有整数解 \(x=\sum\limits_{i=1}^{n}a_iP_it_i\)。通解:\(x+kp(k\in \mathbb{Z})\)

证明:\(P_i\) 是除 \(p_i\) 之外所有模数的倍数,那么最终 \(x\) 加上 \(P_i\) 的倍数就不影响其余方程。又因为 \(a_iP_it_i \equiv a_i \pmod {p_i}\),满足当前方程组,所以代入 \(x\),原方程组成立。

若要求最小正整数解,只需每步对 \(ans=(ans \bmod p+p) \bmod p\),使 \(ans\) 落在 \(0 \sim p-1\) 范围内,就是最小正整数解。

代码实现:

void CRT()
{
	ll p=1,ans=0;
	for(int i=1;i<=n;i++) p*=p[i];
	for(int i=1;i<=n;i++)
	{
		int P=p/p[i],t,y;
		exgcd(P,p[i],t,y);
		ans+=1ll*a[i]*P*t;
	}
}

题外话

机房大佬叶师傅跟我提到过可以用 CRT 还原被取模数。

但我懒,没试过。

扩展中国剩余定理

考虑 \(p_i\) 不两两互质的情况。

假设已经求出前 \(i-1\) 个方程组的一个解 \(x\)。记 \(m=\operatorname{lcm}(p_1,p_2,...,p_{i-1})\),则 \(x+k\cdot m (k\in \mathbb{Z})\) 是前 \(i-1\) 个方程组的通解。

现在要满足 \(x+k\cdot m \equiv a_i \pmod {p_i}\),即 \(k\cdot m \equiv a_i-x \pmod {p_i}\),其中 \(k\) 是未知量。这是一个线性同余方程,可用 exgcd 求解或判断无解。

那么前 \(i\) 个方程组的解就是 \(x+k\cdot m\)

取模技巧:

  1. 在解上述非线性同余方程时,让 \(a_i-x\)\(p_i\) 取模,并变为正数。
  2. 求出新的 \(x\) 解后,对新的 \(m\) 取模。

代码实现:

void exgcd(int a,int b,int &x,int &y,int &d)
{
	if(!b) return x=1,y=0,d=a,void();
	exgcd(b,a%b,x,y,d);
	int t=x;x=y,y=x-a/b*y;
}

void EXCRT()
{
	int m=1;
	for(int i=1;i<=n;i++)
	{
		cin>>a>>p;
		int k,y,d;//d=gcd(m,p)
		a=((a-x)%p+p)%p;//取个模
		exgcd(m,p,k,y,d);
		if(a%d!=0) return puts("No solution"),void();//判断无解
		k=k*a/d;
		x+=k*m;
		m=lcm(m,p);
		x=(x%m+m)%m;
	}
}

高次同余方程

BSGS 算法及扩展

是用来解决形如 \(a^x \equiv b \pmod p\) 这类的问题。

若保证 \(a,p\) 互质,便很容易想到欧拉定理。\(a^{\varphi(p)} \equiv 1 \pmod p\) 也就说明 \(a^x\) 在模 \(p\) 意义下有一个长为 \(\varphi(p)\) 的循环节。

那么得到一种朴素算法:从 \(1 \sim \varphi(p)\) 枚举 \(x\)。由于 \(p\) 是质数,复杂度为 \(O(p)\)

BSGS 是对此算法的改进。其实不难想到分块。

\(x=i\cdot t-j\)(设为减号为了方便),则 \((a^t)^i \equiv b\cdot a^j \pmod p\)

\(t=\sqrt{\varphi(p)}\),不难得出 \(O(\sqrt{p})\) 的算法。

\(b\cdot a^j \bmod p\) 所有可能取值放到一个哈希表里,再枚举 \(i\) 验证 \((a^t)^i\) 是否在表里就可以了。

为方便,直接取 \(t=\sqrt{p}+1\),注意要把 \([1,p-1]\) 里所有值取完,所以 \(i,j\) 范围为 \([1,t]\)。(也可以是其他的,合理即可)

代码实现:

int BSGS()
{
    unordered_map<int,int> mp; //相当于哈希表
    int t=sqrt(p)+1,A=1;
    for(int i=1;i<=t;i++)
        A=A*a%p,mp[b*A%p]=i;
    int now=A;//now=a^t
    for(int i=1;i<=t;i++)
    {
        auto it=mp.find(now);
        if(it!=mp.end()) return i*t-it->second;
        now=now*A%p;
    }
    return -1;
}

\(a,p\) 不互质,我们想办法让其互质。 \(a^x \equiv b \pmod p\) 可写为 \(a\cdot a^{x-1}+p\cdot y=b\)

根据裴蜀定理,当 \(d=\gcd(a,p) \mid b\) 时,方程有整数解。那么方程两边同时除以 \(d\),得到 \(\frac{a\cdot a^{x-1}}{d}+\frac{p\cdot y}{d}=\frac{b}{d}\);也就是 \(\frac{a}{d}a^{x-1} \equiv \frac{b}{d} \pmod {\frac{p}{d}}\)。这时,如果 \(d=1\),那么就可以用 BSGS 了,否则继续递归。当然左侧多了一个系数,在 BSGS 时乘上即可。

注意当 \(x=0\)\(x-1\) 变为负数,故需要特判。即当 \(b=1\) 时直接返回 \(x=0\)

细节很多,上代码:

//k 是系数,注意 BSGS 时也要传
int exBSGS(int a,int b,int p,int k=1)
{
    a%=p,b%=p;
    if(b==1||p==1) return 0;
    int d;
    for(int i=0;;i++)
    {
        d=gcd(a,p);
        if(b%d) return -1;
        b/=d,p/=d;
        k=k*a/d%p;
        if(k==b) return i+1;
        if(d==1) return BSGS(a,b,p,k)+i+1;
    }
}

原根

咕咕咕。

高斯消元

说人话就是解线性方程组。

可参考解二元一次方程组的过程。

直接说做法,可自行模拟。

每个未知数选择一个方程组,用选择的方程组消去其余方程组的当前未知数。

最后每个未知数等于选择的方程组的最终常数。

给一个过程方便理解。

如以下方程组:

\(\begin{cases} k_{1,1}\cdot x_1+k_{1,2}\cdot x_2+k_{1,3}\cdot x_3=d_1 \\ k_{2,1}\cdot x_1+k_{2,2}\cdot x_2+k_{2,3}\cdot x_3=d_2 \\ k_{3,1}\cdot x_1+k_{3,2}\cdot x_2+k_{3,3}\cdot x_3=d_3 \end{cases}\)

先消去第一个未知数:

\(\begin{cases} k_{1,1}\cdot x_1+k_{1,2}\cdot x_2+k_{1,3}\cdot x_3=d_1 \\ 0\cdot x_1+k_{2,2}'\cdot x_2+k_{2,3}'\cdot x_3=d_2' \\ 0\cdot x_1+k_{3,2}'\cdot x_2+k_{3,3}'\cdot x_3=d_3' \end{cases}\)

再消去第二个未知数:

\(\begin{cases} k_{1,1}'\cdot x_1+0\cdot x_2+k_{1,3}'\cdot x_3=d_1' \\ 0\cdot x_1+k_{2,2}'\cdot x_2+k_{2,3}'\cdot x_3=d_2' \\ 0\cdot x_1+0\cdot x_2+k_{3,3}''\cdot x_3=d_3'' \end{cases}\)

再消去第三个未知数:

\(\begin{cases} k_{1,1}''\cdot x_1+0\cdot x_2+0\cdot x_3=d_1'' \\ 0\cdot x_1+k_{2,2}'\cdot x_2+0\cdot x_3=d_2'' \\ 0\cdot x_1+0\cdot x_2+k_{3,3}''\cdot x_3=d_3'' \end{cases}\)

每个未知数的值显而易见了。

如果出现 \(0=d\) 这样的方程:

\(d=0\),则有多个解,当前未知数称为自由元。

\(d \neq 0\),则方程组无解。

那么为了方便,每次找到对于当前未知数含有最大系数的方程组,如果系数 \(=0\),则判断无解。

其实这么说还是很抽象。直接看代码。

void gauss(vector<vector<double>> &a)
{
	//读入系数,a[i][n+1] 是等号后面的常数
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			a[i][j]=rd();
	for(int i=1;i<=n;i++)
	{
		//现在考虑第 i 个未知数
		//寻找对于第 i 个未知数具有最大系数的方程组
		int mx=i;
		//注意前 i-1 个方程组已使用
		for(int j=i+1;j<=n;j++)
			if(fabs(a[j][i]-a[mx][i])>eps) mx=i;
		if(fabs(a[mx][i])<eps) {puts("No solution");return;}//判断无解
		swap(a[i],a[mx]);
		for(int j=1;j<=n;j++)//枚举每个方程组
			if(i!=j)
				for(int k=i+1;k<=n+1;k++)
					a[j][k]-=a[j][i]/a[i][i]*a[i][k];	
	}
	for(int i=1;i<=n;i++) a[i][n+1]/=a[i][i];
}

还有一种遇的比较多的,异或方程组。就是加号变成了异或运算。

那么此时的系数就为 \(0\)\(1\)。加法减法就变为了异或,且不需要乘法。

为方便,可以把每个方程组进行状态压缩。及用一个整数表示。

如果位数过多,存不下,考虑用 bitset 优化常数。

void gauss()
{
	for(int i=1;i<=n;i++)
	{
		//变化一下思路,找最大的 a[i],也就是主元位数最高的 a[i]
		for(int j=i+1;j<=n;j++)
			if(a[j]>a[i]) swap(a[i],a[j]);
		if(a[i]==1) {puts("No solution");return;}//出现 0=1 的方程,无解
		for(int k=n;k;k--)
			if(a[i]>>k&1)//以 a[i] 最高位为主元,消去其他方程该位的系数
			{
				for(int j=1;j<=n;j++)
					if(i!=j&&(a[j]>>k&1)) a[j]^=a[i];
				break;
			}
	}
}
posted @ 2023-06-20 21:53  spider_oyster  阅读(131)  评论(0)    收藏  举报