首先,下面讨论的是数论相关内容。主要研究对象是非负整数。其次,博客不对一些定理进行数学推理,只阐述相关结论或者性质。另外,涉及到相关代码演示,使用的是Java语言。
倒数的概念相信大家都很了解,给定两个数字, a 和 b a和b a和b若: a × b = = 1 a\times b==1 a×b==1
那么 a 和 b a和b a和b互为倒数。
逆元和倒数的概念类似。不过逆元是模运算上“倒数”的概念。 在模m的情况下,给定一个整数 a a a,存在另一个整数 b b b,使得:
a × b ≡ 1 ( m o d m ) a \times b \equiv 1 \pmod{m} a×b≡1(modm)
即, ( a × b ) ( m o d m ) = 1 (a \times b) \pmod m = 1 (a×b)(modm)=1 , 那么b就是a在模m下的乘法逆元。 和倒数的概念类似,a和b互为乘法逆元,但必须是在模m的条件下成立!
举几个栗子,说明什么是逆元:
我们需要找到一个整数 b b b,使得:
3 × b ≡ 1 ( mod 7 ) 3 \times b \equiv 1 \ (\text{mod} \ 7) 3×b≡1 (mod 7)
尝试不同的 b b b 值:
因此, b = 5 b = 5 b=5 是 3 3 3 在模 7 7 7 下的逆元,因为 3 × 5 ≡ 1 ( mod 7 ) 3 \times 5 \equiv 1 \ (\text{mod} \ 7) 3×5≡1 (mod 7)。
我们需要找到 b b b,使得:
2 × b ≡ 1 ( mod 5 ) 2 \times b \equiv 1 \ (\text{mod} \ 5) 2×b≡1 (mod 5)
尝试不同的 b b b 值:
因此, b = 3 b = 3 b=3 是 2 2 2 在模 5 5 5 下的逆元,因为 2 × 3 ≡ 1 ( mod 5 ) 2 \times 3 \equiv 1 \ (\text{mod} \ 5) 2×3≡1 (mod 5)。
我们知道,在数学的模运算中,参数必须是整数(即使在许多编程语言中支持对浮点数进行模运算),因此在模运算中可能出现这种冲突:
3 / 2 ≡ ? ( m o d 107 ) 3 /2 \equiv ? \pmod{107} 3/2≡?(mod107)
3 ÷ 2 = 1.5 \div2=1.5 ÷2=1.5是一个小数,小数不能进行模运算啊。因此我们需要想办法把 ÷ 2 \div2 ÷2等价替换成其他参数,使得 ( 3 ÷ 2 ) (3\div2) (3÷2)这个整体是一个整数,然后再进行模 107 107 107. 而乘法逆元就可以完美做到这一点。
根据乘法逆元的定义,我们设 a a a是 2 2 2在模m的情况下的乘法逆元:
a × 2 ≡ 1 ( m o d 107 ) a \times 2 \equiv 1 \pmod{107} a×2≡1(mod107)
式子两边同时除以2:
a ≡ 1 / 2 ( m o d 107 ) a \equiv 1/2 \pmod{107} a≡1/2(mod107)
这时,我们就可以把原式中【 3 / 2 ≡ ? ( m o d 107 ) 3 /2 \equiv ? \pmod{107} 3/2≡?(mod107)】 的1/2替换成 a a a:
3 × a ≡ ? ( m o d 107 ) 3 \times a \equiv ? \pmod{107} 3×a≡?(mod107)
根据计算,2在模107下的乘法逆元是54,所以 3 × 54 ≡ ? ( m o d 107 ) → 162 ≡ 55 ( m o d 107 ) 3 \times 54 \equiv ? \pmod{107} \rightarrow 162 \equiv 55 \pmod{107} 3×54≡?(mod107)→162≡55(mod107) 余数55就计算出来了。
a
和m
的最大公约数等于gcd(a,m)=1
,a
在模m
的情况下的乘法逆元b
才存在。m
的情况下,a
的乘法逆元b
存在,那么整数b
一定是唯一的。小定理,快速理解:
费马小定理是数论中的一个重要定理,用于求解模运算下的逆元。对于一个质数 p ,如果 ( a ) 是一个整数且与 ( p ) 互质(即 gcd(a, p) = 1 ),费马小定理指出:
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap−1≡1(modp)
这就是费马小定理。
根据这个定理我们可以求出,a在模p下的逆元,因为上述式子可以被改写成这样:
a ⋅ a p − 2 ≡ 1 ( m o d p ) a \cdot a^{p-2} \equiv 1 \pmod{p} a⋅ap−2≡1(modp)
根据乘法逆元的定义,ap-2模p 就是 a a a在模 p p p的情况下的乘法逆元。
当我们要求一个整数a在模p的情况下的乘法逆元,并且p是质数,gcd(a,p)=1,那么可以运用费马小定理求解,ap-2模p 就是a在模p的情况下的乘法逆元。 时间复杂度是 O ( log ( p ) ) O(\log(p)) O(log(p)) ,因为可以使用快速幂去求解ap-2 。
费马小定理求逆元
AC代码:
import java.util.Scanner;
public class Test{
/**
* 计算a^(p-2) (mod m)
* */
private static int pow(long base,long x,long m){
long result=1;
while(x>0){
if((x&1)==1){
result*=base;
result%=m;
}
base*=base;
base%=m;
x>>=1;
}
return (int)result;
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int p=sc.nextInt();
int ans=pow(n,p-2,p);
System.out.println(ans);
}
}
注意:
观看这里,建议先了解裴蜀定理,了解定义即可。
扩展欧几里得算法不仅可以用来计算两个数的最大公约数,还可以找到这两个数的线性组合。例如,对于给定的两个整数 a a a 和 b b b,扩展欧几里得算法可以找到整数 x x x 和 y y y 使得:
a × x + b × y = gcd ( a , b ) a \times x + b \times y = \text{gcd}(a, b) a×x+b×y=gcd(a,b)
当 a a a 和 m m m 互质(即 gcd ( a , m ) = 1 \text{gcd}(a, m) = 1 gcd(a,m)=1)时,可以找到整数 x x x 使得:
a × x ≡ 1 ( m o d m ) a \times x \equiv 1 \pmod{m} a×x≡1(modm)
这意味着 x x x 就是 a a a 在模 m m m 下的乘法逆元!
如果我们要计算a在模m的情况下的乘法逆元:
使用扩展欧几里得算法计算 a a a 和 m m m 的最大公约数 gcd ( a , m ) \text{gcd}(a, m) gcd(a,m)。如果 gcd ( a , m ) ≠ 1 \text{gcd}(a, m) \neq 1 gcd(a,m)=1,则 a a a 在模 m m m 下的逆元不存在。
如果 gcd ( a , m ) = 1 \text{gcd}(a, m) = 1 gcd(a,m)=1,扩展欧几里得算法会找到整数 x x x 和 y y y,使得:
a × x + m × y = g c d ( a , m ) = 1 ( m o d m ) a \times x + m \times y = gcd(a,m)=1 \pmod{m} a×x+m×y=gcd(a,m)=1(modm)
进一步改写:
a × x + m × y = 1 ( m o d m ) a \times x + m \times y= 1 \pmod{m} a×x+m×y=1(modm)
因为右边第二项模m为0,所以在进一步改写:
a × x = 1 ( m o d m ) a \times x = 1 \pmod{m} a×x=1(modm)
根据这个等式,便得出 a × x ≡ 1 ( m o d m ) a \times x \equiv 1 \pmod{m} a×x≡1(modm),因此 x x x 就是 a a a 在模 m m m 下的乘法逆元。 如果对x的求解方式不太了解,请看裴蜀定理详细介绍 中的二.2节,包含证明以及求x得公式。
当我们要求一个整数a在模p的情况下的乘法逆元,并且gcd(a,p)=1,那么可以使用扩展欧几里得算法求解。 裴蜀定理中得系数x
,就是我的答案(当然如果x是负数,或者是p的倍数,进行模运算的处理即可,具体可以看模板题的操作)
AC代码
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static class ResultGCD{
int x;
int y;
int gcd;
public ResultGCD(int xx,int yy,int gg){
x=xx;
y=yy;
gcd=gg;
}
}
private static ResultGCD exGcd(int a,int b){
if(b==0)return new ResultGCD(1,0,a);
ResultGCD result=exGcd(b,a%b);
int gcd=result.gcd;
int xx=result.x;
int yy=result.y;
int x=yy;
int y=xx-(a/b)*yy;
return new ResultGCD(x,y,gcd);
}
private static int getInvers(int a,int m){
ResultGCD ans=exGcd(a,m);
if(ans.gcd==1){
//特殊处理
return (ans.x%m+m)%m;
}else return -1;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
int n=scan.nextInt();
for(int i=0;i<n;i++){
int a=scan.nextInt();
int b=scan.nextInt();
int ans=getInvers(a,b);
System.out.println(ans);
}
scan.close();
}
}
递推公式法是一种求解连续整数逆元的快速方法,适用于模 p p p 是质数的情况。
递推公式法适用于要求从 1 1 1 到 n n n 的所有整数在模 p p p 下的逆元,其中 p p p 是质数。通过递推,我们可以快速地求得这些逆元。递推公式如下:
inv [ i ] = ( p − p / i ) × inv [ p m o d i ] ( m o d p ) \text{inv}[i] = (p - p / i) \times \text{inv}[p \bmod i] \pmod{p} inv[i]=(p−p/i)×inv[pmodi](modp)
inv[i] = (p - (p / i) * inv[p % i] % p) % p;
其中, inv [ i ] \text{inv}[i] inv[i] 表示整数 i i i 在模 p p p 下的乘法逆元, p p p 是质数。
假设我们已经知道了 1 , 2 , … , i − 1 1, 2, \dots, i-1 1,2,…,i−1 的逆元,接下来我们来求 i i i 的逆元。
我们可以通过以下关系得到:
根据扩展欧几里得原理,可以得到:
i × inv [ i ] ≡ 1 ( m o d p ) i \times \text{inv}[i] \equiv 1 \pmod{p} i×inv[i]≡1(modp)
可以重写为:
i × inv [ i ] + p × k = 1 i \times \text{inv}[i] + p \times k = 1 i×inv[i]+p×k=1
通过递推可以求出所有整数的逆元。
假设我们要找出从 1 1 1 到 6 6 6 在模 7 7 7 下的逆元:
这样,我们就可以通过递推公式快速地求出一系列整数的逆元。
对于求解逆元的问题,我们有三种常用的方法:
根据实际应用场景,可以选择合适的方法来求解逆元,以简化计算过程并提高效率。