同余问题

前言

我发现我的数论知识整理 , 整理的真的很乱,我觉得有必要重新整理梳理一下,就先从同余开始吧,前面的也没有什么可以梳理的,就是比较简单的结论和证明。(但也不代表本章很难,笔者是个 \(fw\) ,所以整理不出很 \(nb\) 的东西)

这篇文章能够提供什么学习内容,即这篇文章你能够学到什么 :

  • \(1.\) 欧拉函数 \(\text{&}\) 欧拉定理
  • \(2.\) 乘法逆元
  • \(3.\) 扩展欧几里得
  • \(4.\) 同余方程
  • \(5.\) 中国剩余定理

一些表示

  • \(1.\) \(\equiv\) 表示的模意义下相等
  • \(2.\) \(\varphi\) 表示欧拉函数
  • \(3.\) \(p\) 无特殊说明为一个质数。

同余的性质

  • \(1.\) 自反性:\(a \equiv a (mod \ \ p )\)
  • \(2.\) 对称性:\(a \equiv b, b \equiv a (mod \ \ p)\)
  • \(3.\) 传递性:\(a\equiv b ,b\equiv c , a\equiv c(mod \ \ p)\)
  • \(4.\) 满足运算律 \(a \equiv b , c \equiv d , a\bigoplus c \equiv b\bigoplus d(mod \ \ p)\)

\(\bigoplus\) 代表运算。

虽然说很显然,但是也是需要放在前面的。

还有就是,对负数取模的时候, \(ret = (ret + mod) \text{%} mod\) , 当然,不要 \(define\ \ int \ \ unsigned\ \ long\ \ long\) , 该事例来源于机房某憨批。

欧拉函数 & 欧拉定理

首先声明,欧拉函数是没有欧拉定理的。

欧拉函数

\(\varphi(N)\) 为欧拉函数,为 \([1,N]\) 中与 \(N\) 互质的数的个数。
一个小定理 :

  • \(N = \prod_{\rm{p|N}}{p_i}^{c_i}\)
    \(\varphi(N) = N \times \prod_{p|N}(1 - \frac{1}{p})\)

证明 : 涉及一个容斥原理。
我们设 \(N\) 的一个质因子为 \(p\) , 那么 \(p\) 的倍数有 \(\frac{N}{p}\) 个,同理,若 \(q\) 也是 \(N\) 的一个质因子,那么 \(q\) 的倍数有 \(\frac{N}{q}\) 个,
我们去掉这些数 (因为不能有互质的,\(N\) 和他们的最大公约数都不是,是 \(p\)\(q\)) .
那么我们就删掉了 \(pq\) 的倍数两次,给它加回来(容斥) 。
那么最后我们能得到 \(N - \frac{N}{p} - \frac{N}{q} + \frac{N}{pq} = N\times (1 - \frac{1}{p})\times (1 - \frac{1}{q})\)

如果需要计算欧拉函数的话,我们就根据上述,直接搞一个质因数的分解,顺带求出欧拉函数。

欧拉定理

若正整数 \(a , n\) 互质,则 \(a^{\varphi(n)} \equiv 1(mod \ \ p)\)

证明 : 咕了。

欧拉定理的推论

若正整数 \(a,n\) 互质,则对于任意的正整数 \(b\) ,有 \(a^b \equiv a^{b \ \ mod \ \ \varphi(p)} (mod \ \ p)\)

证明:来源:抄书
\(b = q \times \varphi(p) + r\) , 其中 $ 0 \leq r < \varphi(p) $ , 即 \(r = b \ \ mod \ \ \varphi(p)\)
\(a^b \equiv a^{q\times \varphi(p) + r}\equiv a^{\varphi(p)^q}\times a^r \equiv 1^q \times a^r \equiv a^r \equiv a^{b \ \ mod \ \ \varphi(p)} (mod \ \ p)\)
得证。

扩展欧拉定理

\[a^b \equiv \begin{cases} a^b &b \leq \varphi(p) \\ a^{b \ \ mod \ \ \varphi(p) + \varphi(p)}, & b > \varphi(p) \end{cases} ( \ \ mod \ \ p) \]

证明 : 且咕。

乘法逆元

为什么这里要说一下乘法逆元呢? 首先是这里有时候会

因为在模运算中,是不支持除法的,但是我们可以将 \(\frac{a}{b}\) 转化成 \(a\times b^{-1}\) 的乘法,那么我们这个时候只需要求解出 \(b^{-1}\) 就好了,你可能会说,这不就是 \(\frac{1}{b}\) 吗? 的确,但是你要明白的是,模运算不支持除法。所以,我们求解 \(b^{-1}\) 就不能直接 \(\frac{1}{b}\)

  • 事先说明 \(b^{-1}\) 已经不是 \(\frac{1}{b}\) ,而是代表着 \(mod \ \ p\) 意义下的逆元。

我们继续探寻一下这个逆元的性质 ,也就是 \(b \times b^{-1} \equiv 1(mod \ \ p)\)

我们探讨一下求解的方法

递推式法

确切地来说,这种方法是最容易理解的一种了。 推点式子嘛。

我们假设 \(p = k \times i + r\) , 则显然是有

\[k \times i + r \equiv 0 \ \ (\ \ mod \ \ p \ \ ) \]

\[k \times i \times i^{-1}\times r^{-1} + r \times r^{-1} \times i^{-1}\equiv 0 (mod \ \ p) \]

\[k \times r^{-1}+ i^{-1} \equiv 0(mod \ \ p) \]

\[i^{-1} \equiv -k\times r^{-1} (mod \ \ p) \]

那么显然我们同样是可以知道的, \(k = \lfloor\frac{p}{i}\rfloor , r = p \text{%} i\)

那么我们显然 \(r < i\) ,那么我们就有递推式

\[f_i = -\lfloor\frac{p}{i}\rfloor \times f_{p\text{%}i} \]

inv[1] =1 ; 
for(qwq int i = 2 ; i <= n ; i++) 
 inv[i] = (- p / i + p) * f[p % i] ;

费马小定理求解逆元

\[a^{-1} = a^{p-1} (mod \ \ p) \]

这里必须保证 \(p\) 为质数。

证明:咕了。

上文还有一个欧拉定理求解的。
下文还有一个扩展欧几里得求解的。

扩展欧几里得

首先你需要明白什么是欧几里得。

裴蜀定理

对于任意的正整数 \(a , b\) 若存在正整数 \(x ,y\) 使得式子 \(ax + by = m\) , 则 \(gcd(a ,b)|m\)

证明 : 我们不断的递归 \(gcd(a , b)\) ,显然递归到最后一层 ,就是当 \(x = 1 , b = 0\) 的时候,有一个特殊解 , 无论 \(a\) 取什么值,都有 \(a \times 1 + 0 \times 0 = gcd(0 , a)\) .
然后求解 \(gcd\) 的过程是递归回溯找到的,那么也就会有 \(gcd(b , a \text{%} b) = gcd(a , b)\) .现在我们假设存在一组解 \(x , y\),使得其一定满足于 \(b\times x + (a \text{%}b)\times y = gcd(b , a \text{%} b)\) , 则又因为 \(a \text{%} b = a - \lfloor\frac{a}{b}\rfloor\times b\) , 带入式子我们就有

\[b\times x + (a - \lfloor\frac{a}{b}\rfloor\times b)\times y = gcd(a , a \text{%}b) \]

这也就等价于

\[bx + (a - \lfloor\frac{a}{b}\rfloor\times b) \times y <=> ay - b\times (x - \lfloor\frac{a}{b}\rfloor)\times y \]

然后我们就得到了通解,继续向上不断的寻找,递归到顶部,最后一定能实现,这时候我们不仅证出了结论,亦求解了出来。

  • 应用
  • \(1.\) 判断不定方程 \(ax + by = m\) 是否有解
  • \(2.\) 求解出不定方程 \(ax + by = m\) 的解。

扩展欧几里得求解逆元

\[a\times a^{-1} \equiv 1(mod \ \ p) <=> a \times x + py = 1 \]

扩展欧几里得求解逆元其实就是对应着求解不定方程对应着 \(1\) 的情况 。 直接求解即可。 这里给出扩展欧几里得求解不定方程的代码 :

void exgcd(int a , int b , int &x , int &y) //这里直接传下去,就不用设置全局变量了
{
	if(!b) return (void) (x = 1 , y = 0) ;//某人竟然以为会CE
	exgcd(b , a % b , x , y) ; 
	int k = x ; x = y ; y = k - y * (a / b) ; 
}

扩展欧几里得求解线性同余方程

我们这货和求解逆元长得很像,其实求解逆元就是这个一个特例。

\[ax \equiv c (mod \ \ p) \]

这个同余方程等价于 \(ax + py = c\)

然后这货就和不定方程一样了。 直接 \(exgcd\) 求解即可。

中国剩余定理

又称孙子定理。(什么傻逼名字)

普通中国剩余定理

中国剩余定理也就是让你求解一个线性同余方程组。
即为:

\[ \begin{cases} x \equiv a_1 &( mod \ \ p_1 )\\ x \equiv a_2 & (mod \ \ p_2) \\ …… \\x \equiv a_n & (mod \ \ p_n) \end{cases}\]

如果 \(p_i\) 均互质,那么我们称其为中国剩余定理,当然如果不互质,我们叫他扩展中国剩余定理,最后求解出来的是 \(x\) 的最小值。

  • 一些变量设置 :
  • \(1.\) \(M = \prod_{i = 1}^{n}p_i\)
  • \(2.\) \(m_i = \frac{M}{p_i}\)
  • \(3.\) \(inv_i\) 表示 \(m_i\)\(mod \ \ M\) 意义下的逆元。

\(x = \sum_{i = 1}^{n} m_i \times inv_i \times a_i\)

证明证明这东西搞懂搞不懂都行,记住也可以
\(emm\), 我们直接选择带入反向验证是否是正确的,只要是正确的,那么我最后是 \(mod \ \ M\) 的 ,最小值显然是不必担心的。我们发现 \(\forall k \neq i , a_i \times inv_i\times m_i \equiv 0 (mod \ \ p_k)\) , 带入则显然是成立的。

甚至说,中国剩余定理你都可以不用掌握,直接来到 \(excrt\) 也是可以的。

void exgcd(int a , int b , int &x , int &y) 
{
	if(!b) return (void) (x = 1 , y = 0) ; 
	exgcd(b , a % b , x , y) ; 
	int k = x ; x = y ; y = k - y * (a / b) ; 
}
void init() 
{
	for(qwq int i = 1 ; i <= n ; i++)
	{
		a[i] = read() , p[i] = read() ; //剩余 a_i 条猪
		M = M * p[i] ; 
	}
	for(qwq int i = 1 ; i <= n ; i++) 
	{
		int mi = M / p[i] ; 
		exgcd(mi , p[i] , x , y) ; 
		s = (s + mi * x * a[i]) % M ; 
	}
}

扩展中国剩余定理

这个时候还是求解线性同余方程组

\[ \begin{cases} x \equiv a_1 &( mod \ \ p_1 )\\ x \equiv a_2 & (mod \ \ p_2) \\ …… \\x \equiv a_n & (mod \ \ p_n) \end{cases}\]

但是这时候已经不满足 \(p_i\) 互质了,我们考虑这个问题就只有一个我们是否可以修复呢?很显然答案是否定的。因为在 \(p_i\) 不互质的情况下, \(inv\) 可能不存在导致整个算法是错误的。

既然都是合并同余方程组来解决问题的,我们同样也是来合并一下。根据线性同余方程我们可以知道上面的两个式子等价于

\[\begin{cases} x - p_1 k_1 = a_1 \\ x - p_2 k_2 = a_2\end{cases} \]

我们就知道 \(a_1 + p_1k_1 = a_2 + p_2k_2\) , 然后我们继续随便搞一下 \(p1k_1 - p_2 k_2 = a_1 - a_2\) , 我们发现这个和上述 \(ax + by = c\) 有着相同的形式,其中 \(a_1 , a_2 ,p_1 ,p_2\) 均为已知量,在模 \(\frac{p_1p_2}{gcd(p_1 , p_2)}\) 时成立。

然后我们就有了一个 \(x = k_1p_1(mod \ \ (\frac{p_1p_2}{gcd(p_1 , p_2)}))\)

inline int poow(int a , int b , int mod) 
{
	int ret = 0 ; 
	while(b) 
	{
		if(b & 1) ret = ( ret + a ) % mod ; 
		a = a + a % mod ;
		b >>= 1 ;  
	}
	return ret ; 
}
inline void exgcd(int a , int b , int &x , int &y) 
{
	if(!b) 
	{
		x = 1 , y = 0 ;
		return ; 	
	}
	exgcd(b , a % b , x , y) ; 
	int k = x ; 
	x = y ;
	y = k - (a / b) * y ; 
}
inline int query() 
{
	int M = a[1] , ans = b[1] , x , y; 
	for(int i = 2 ; i <= n ; i++) 
	{
		int k = M , l = a[i] , c = (b[i] - ans % l + l) % l ; 
		int gcd = Base::Gcd(k , l) , lcm = l / gcd ; 
		exgcd(k , l , x , y) ;
		if(c % gcd != 0 ) return -1 ; 
		x = poow(x , c / gcd , lcm) ; 
		ans += x * M ;
		M *= lcm ; 
		ans = (ans % M + M) % M ;
	} 
	return (ans % M + M) % M ; 
}
posted @ 2021-04-25 22:23  SkyFairy  阅读(197)  评论(0编辑  收藏  举报