【算法学习】逆元与快速幂

逆元与快速幂

逆元的数学原理与其在快速幂中的应用 原题为蓝桥杯国赛 小数的第n位

总结算法的学习过程:这是一个什么样的原理,我如何将其他问题转化为这样的算法

逆元

单位元

【定义1】在一个集合中,对于某种运算 ∗ * (注意:这里代表通用运算的表示符号,并不是特指乘法),如果对于任何的集合元素 a a a ,和元素 e e e运算,得到还是集合元素 a a a本身,则称 e e e 为这个运算下的单位元

加法中的单位元为 a + e = a , e = 0 a + e = a, e = 0 a+e=a,e=0

乘法中的单位元为 a × e = a , e = 1 a \times e = a, e = 1 a×e=a,e=1

逆元

【定义2】在一个集合中,对于某种运算 ∗ * ,如果任意两个元素的运算结果等于单位元,则称这两个元素互为逆元。

在mod的意义下,不能直接除以一个数,而要除以它的逆元

Example:
a ∗ b ≡ 1 ( m o d   p ) ⇔ x / b = x ∗ b % p a * b \equiv 1(mod\ p)\\ \Leftrightarrow x / b = x * b \% p ab1(mod p)x/b=xb%p

加法运算中逆元 a + a − 1 = e = 0 , a − 1 = − a a + a^{-1} = e = 0, a^{-1} = -a a+a1=e=0,a1=a

乘法运算中逆元 a × a − 1 = e = 1 , a − 1 = 1 a a \times a^{-1} = e = 1, a^{-1} = \frac{1}{a} a×a1=e=1,a1=a1

模乘中的单位元

对于 n n n的乘法,所有模 n n n a a a同余的数都可以表示成:

a ( m o d   n ) = k n + a ( k ∈ Z ) a(mod\ n) = kn + a (k \in \Z) a(mod n)=kn+a(kZ)

另单位元为 e ( m o d   n ) e(mod\ n) e(mod n)则根据单位元的概念可得

a ( m o d   n ) × e ( m o d   n ) = a ( m o d   n ) ⇒ ( k 1 k 2 n + k 1 e + k 2 ) n + e a = k n + a ⇒ { k = k 1 k 2 n + k 1 e + k 2 e = 1 a(mod\ n) \times e(mod\ n) = a(mod\ n) \\ \Rightarrow (k_1 k_2 n + k_1 e + k_2)n + ea = kn + a\\ \Rightarrow \begin{cases} k = k_1 k_2 n + k_1 e + k_2 \\ e = 1 \end{cases} a(mod n)×e(mod n)=a(mod n)(k1k2n+k1e+k2)n+ea=kn+a{k=k1k2n+k1e+k2e=1

模乘中的逆元

因为对于任意模 n n n a a a同余的数的单位元都是 1 ( m o d   n ) 1(mod\ n) 1(mod n)

要找 a ( m o d   n ) a(mod\ n) a(mod n)的逆元 a − 1 ( m o d   n ) a^{-1}(mod\ n) a1(mod n)就要求两个元素满足

a ( m o d   n ) × a − 1 ( m o d   n ) = 1 ( m o d   n ) a ( m o d   n ) = k 1 n + a a − 1 ( m o d   n ) = k 2 n + a − 1 a(mod\ n) \times a^{-1}(mod\ n) = 1(mod\ n)\\ a(mod\ n) = k_1 n + a \quad a^{-1}(mod\ n) = k_2 n + a^{-1} a(mod n)×a1(mod n)=1(mod n)a(mod n)=k1n+aa1(mod n)=k2n+a1

也就是说要满足

( k 1 k 2 n + k 1 a − 1 + k 2 a ) n + a a − 1 = k 3 n + 1 ⇒ { k 1 k 2 n + k 1 a − 1 + k 2 a − k 3 = 0 a a − 1 − 1 = 0 Consequently: a a − 1 + k ⋅ n = 1 (k_1k_2n + k_1a^{-1} + k_2a)n + aa^{-1} = k_3n + 1 \\ \Rightarrow \begin{cases} k_1k_2n + k_1a^{-1} + k_2a -k_3 = 0 \\ aa^{-1} - 1= 0 \end{cases} \\ \text{Consequently:\quad} aa^{-1} + k \cdot n = 1 (k1k2n+k1a1+k2a)n+aa1=k3n+1{k1k2n+k1a1+k2ak3=0aa11=0Consequently:aa1+kn=1

其中 a , n a,n a,n已知,那么可以将求模的逆的问题转化成:

对于给定的正整数 a a a b b b,求解下列方程中 x x x的最小整数解:( x x x即为 a a a的逆元)
a x + b y = 1 ax + by = 1 ax+by=1

即欧几里德定理

思想: 整数域上的运算与一元多项式的运算是同构的,于是考虑的时候可以把一元多项式的相关结论拿出来用

求解过程:
( 1 ) a x + b y = 1 ( 2 ) = gcd ⁡ ( a , b ) ( 3 ) = gcd ⁡ ( b , a ( m o d   b ) ) ( 4 ) = b x ′ + ( a   m o d   b ) y ′ ( 5 ) = b x ′ + ( a − b ⌊ a b ⌋ ) y ′ ( 6 ) = a y ′ + b ( x ′ − ⌊ a b ⌋ ) \begin{aligned} (1)&&\quad ax + by &= 1 \\ (2)&&\quad &= \gcd(a,b) \\ (3)&&\quad &=\gcd(b, a(mod\ b)) \\ (4)&&\quad &=bx' + (a\ mod\ b)y' \\ (5)&&\quad &=bx' + (a - b\lfloor \frac{a}{b}\rfloor)y' \\ (6)&&\quad &=ay' + b(x' - \lfloor\frac{a}{b} \rfloor) \end{aligned} (1)(2)(3)(4)(5)(6)ax+by=1=gcd(a,b)=gcd(b,a(mod b))=bx+(a mod b)y=bx+(abba⌋)y=ay+b(xba⌋)

公式解释:
(1)题目给定等式
(2)a,b互素,则 1 = gcd ⁡ ( a , b ) 1 = \gcd(a,b) 1=gcd(a,b)
(3)欧几里德定理,辗转相除法
(4)写出 b b b a ( m o d   b ) a(mod\ b) a(mod b)的欧几里德定理形式
(5)根据取模的定义 a m o d    b = a − b ⌊ a b ⌋ a\mod b = a - b\lfloor\frac{a}{b} \rfloor amodb=abba(其中 ⌊ a b ⌋ \lfloor\frac{a}{b} \rfloor ba表示向下取整)
(6)整理成关于a,b系数的形式
⇒ a x + b y = a y ′ + b ( x ′ − ⌊ a b ⌋ ) ⇒ { x = y ′ y = ( x ′ − ⌊ a b ⌋ ) \Rightarrow ax+ by = ay' + b(x' - \lfloor \frac{a}{b} \rfloor) \\ \Rightarrow \begin{cases} x = y'\\ y = (x' - \lfloor \frac{a}{b} \rfloor) \end{cases} ax+by=ay+b(xba⌋){x=yy=(xba⌋)

得到了辗转相除法的递推公式,代码实现
编写成代码得:

typedef long long ll;
int EXgcd(ll a, ll b, ll &x, ll &y)
{
    ll q, tmp
    if(!b)
    {
        q = a;
        x = 1;
        y = 0;
    }
    else
    {
        q = EXgcd(b, a%b, x, y);
        temp = x;
        x = y;
        y = temp - (a / b) * y;
    }
    return q;
}

求逆元题的标准形式

  1. 【例题1】给定正整数 a a a b b b,求满足等式 a x + b y = 1 ax + by = 1 ax+by=1 x x x的最小正整数解。如果不存在返回 -1。

  2. 【例题2】给定正整数 a a a n n n,求满足等式 a x ≡ 1 ( m o d   n ) ax \equiv 1(mod\ n) ax1(mod n) x x x的最小正整数解。如果不存在,则返回-1。

  3. 【例题3】给定素数 p p p和正整数 a a a,求满足 a ≡ 1 ( m o d   p ) a\equiv 1(mod\ p) a1(mod p)的最小正整数 x x x,如果不存在返回-1。

对于例题3证明: 会用到二分快速幂

求逆元的方法

方法一:扩展欧几里得算法

公式原理:
a ∗ b ≡ 1 ( m o d   p ) a ∗ b + k ∗ p = 1 a * b \equiv 1(mod\ p) \\ a *b +k * p = 1 ab1(mod p)ab+kp=1

其中 a a a为要求的逆元(其中b,p为已知),用辗转相除法求解。

辗转相除法(递归算法)递归推导过程见上
下以多项式证明(多项式空间与整数空间同构)
g e t   g r e a t e s t   c o m m o n   d i v i s o r : f ( x ) = q 1 ( x ) g ( x ) + r 1 ( x ) g ( x ) = q 2 ( x ) r 1 ( x ) + r 2 ( x ) . . . r s − 3 ( x ) = q s − 1 ( x ) r s − 2 ( x ) + r s − 1 ( x ) r s − 2 ( x ) = q s ( x ) r s − 1 ( x ) + r s ( x ) r s − 1 ( x ) = q s + 1 ( x ) r s ( x ) + 0 ⇒ r s ( x )   i s   g r e a t e s t   c o m m o n   d i v i s o r   f i n d   u ( x )   a n d   v ( x ) : r s ( x ) = r s − 2 ( x ) − q s ( x ) r s − 1 ( x ) r s − 2 ( x ) = q s ( x ) r s − 1 ( x ) + r s ( x ) ⇒ r s ( x ) = q s ( x ) r s − 1 ( x ) + r s ( x ) − q s ( x ) r s − 1 ( x ) r s − 1 ( x ) = r s − 3 ( x ) − q s − 1 ( x ) r s − 2 ( x ) ⇒ r s ( x ) = q s ( x ) r s − 3 ( x ) − q s − 1 ( x ) r s − 2 ( x ) + r s ( x ) − q s ( x ) r s − 3 ( x ) − q s − 1 ( x ) r s − 2 ( x ) . . . r 2 ( x ) = g ( x ) − q 1 ( x ) r 1 ( x ) r 1 ( x ) = f ( x ) − q 1 ( x ) g ( x ) get \ greatest \ common \ divisor:\\ f(x) = q_1(x) g(x) + r_1(x) \\ g(x) = q_2(x) r_1(x) + r_2(x) \\ ... \\ r_{s-3}(x) = q_{s-1}(x) r_{s-2}(x) + r_{s-1}(x) \\ r_{s-2}(x) = q_{s}(x) r_{s-1}(x) + r_{s}(x) \\ r_{s-1}(x) = q_{s+1}(x) r_{s}(x) +0 \\ \Rightarrow r_{s}(x) \ is \ greatest \ common \ divisor \\ \ \\ find \ u(x) \ and \ v(x): \\ r_s(x) = r_{s-2}(x) - q_s(x)r_{s-1}(x) \\ r_{s-2}(x) = q_{s}(x) r_{s-1}(x) + r_{s}(x) \\ \Rightarrow r_s(x) = q_{s}(x) r_{s-1}(x) + r_{s}(x)- q_s(x)r_{s-1}(x) \\ r_{s-1}(x) = r_{s-3}(x) - q_{s-1}(x) r_{s-2}(x) \\ \Rightarrow r_s(x) = q_{s}(x) r_{s-3}(x) - q_{s-1}(x) r_{s-2}(x) + r_{s}(x)- q_s(x)r_{s-3}(x) - q_{s-1}(x) r_{s-2}(x)\\ ... \\ r_2(x) = g(x) - q_1(x)r_1(x) \\ r_1(x) = f(x) - q_1(x)g(x) get greatest common divisor:f(x)=q1(x)g(x)+r1(x)g(x)=q2(x)r1(x)+r2(x)...rs3(x)=qs1(x)rs2(x)+rs1(x)rs2(x)=qs(x)rs1(x)+rs(x)rs1(x)=qs+1(x)rs(x)+0rs(x) is greatest common divisor find u(x) and v(x):rs(x)=rs2(x)qs(x)rs1(x)rs2(x)=qs(x)rs1(x)+rs(x)rs(x)=qs(x)rs1(x)+rs(x)qs(x)rs1(x)rs1(x)=rs3(x)qs1(x)rs2(x)rs(x)=qs(x)rs3(x)qs1(x)rs2(x)+rs(x)qs(x)rs3(x)qs1(x)rs2(x)...r2(x)=g(x)q1(x)r1(x)r1(x)=f(x)q1(x)g(x)

代码如下:

typedef long long ll;

ll ExpGcd(ll a, ll b, ll &x, ll &y)
{
    ll q,tmp;
    //边界条件
    if(!b)
    {
        q = a;
        x = 1;
        y = 0;
    }
    // 被除数(作新a)
    // 商(作新b)
    // x - ay后的值(作新x)
    // 余式(作新y)
    else
    {
        q = ExpGcd(b, a % b, x, y);
        tmp = x;
        x = y;
        y = tmp - (a / b) * y;
    }
    return q;
}

例如,对27a + 8k = 1方程(b = 27,p = 8)求解过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3ZgpM9R-1680699079110)(./pic/divisor.png)]

费马小定理/欧拉定理

指数形式的逆元求法,一般式子中带有%

费马小定理原理:
if   p  is even, then  a − 1 ≡ 1 ( m o d   p ) ⇒ a p − 2 ≡ 1 ( m o d   p ) then  a p − 2  is the inverse element for a in mod p \text{if }\ p \text{ is even, then } a^{-1} \equiv 1(mod\ p)\\ \Rightarrow a^{p - 2} \equiv 1(mod\ p) \\ \text{then } a^{p - 2} \text{ is the inverse element for a in mod p} if  p is even, then a11(mod p)ap21(mod p)then ap2 is the inverse element for a in mod p

欧拉定理原理:
if  a , p  are comprime then  a φ ( p ) ≡ 1 ( m o d   p )  Fermat’s Little Theorem a φ ( p ) − 1 ∗ a ≡ 1 ( m o d   p ) a φ ( p ) − 1  is the inverse element for a in mod p \text{if }a,p \text{ are comprime then }a^{\varphi(p)} \equiv 1(mod\ p)\text{ Fermat's Little Theorem} \\ a^{\varphi(p) - 1} *a \equiv 1(mod\ p) \\ a^{\varphi(p) - 1}\text{ is the inverse element for a in mod p} if a,p are comprime then aφ(p)1(mod p) Fermat’s Little Theoremaφ(p)1a1(mod p)aφ(p)1 is the inverse element for a in mod p

代码如下:

typedef long long ll;

//快速幂求解a^{p - 1}次幂
ll qkpow(ll a, ll p, ll mod)
{
    ll t = 1, tt = a % mod;
    while(p)
    {
        if(p & 1) t = t * tt % mod;
        tt = tt* tt % mod;
        p >>= 1;
    }
    return t;
}

llgetInv(ll a, ll mod)
{
    return qkpow(a, mod - 2, mod);
}

递推求逆元

用于求很大范围内所有数的逆元(上述方法可能会超时)

原理:
set Mod  m = k × a + b then  k × a + b ≡ 0 ( m o d   m ) multiplies  i n v ( a ) × i n v ( b )  on both side ⇒ k × i n v ( b ) + i n v ( a ) ≡ 0 ( m o d   m ) ∴ i n v ( a ) = − k × i n v ( b ) ( m o d   m ) ∵ k = m / a , b = m % a ⇔ i n v ( a ) = − ( m / a ) × ( m % a ) ( m o d   m ) \text{set Mod }m = k \times a + b \\ \text{then } k \times a + b \equiv 0(mod\ m) \\ \text{multiplies } inv(a) \times inv(b) \text{ on both side} \\ \Rightarrow k \times inv(b) +inv(a) \equiv 0 (mod\ m) \\ \therefore inv (a) = -k \times inv(b)(mod\ m) \\ \because k = m / a , b = m \% a \\ \Leftrightarrow inv(a) = -(m/a) \times (m \%a)(mod\ m) set Mod m=k×a+bthen k×a+b0(mod m)multiplies inv(a)×inv(b) on both sidek×inv(b)+inv(a)0(mod m)inv(a)=k×inv(b)(mod m)k=m/a,b=m%ainv(a)=(m/a)×(m%a)(mod m)

那么递推公式:
i n v ( a ) = − ( m / a ) × ( m % a ) ( m o d   m ) inv(a) = -(m/a) \times (m \%a)(mod\ m) inv(a)=(m/a)×(m%a)(mod m)

同余式(congruence);
i n v ( ) inv() inv() 为求逆;

快速幂

二分递推的思想:
a n = { a n − 1 ⋅ a ,  if n is odd a n 2 ⋅ a n 2 ,  if n is even but not 0 1 , if n = 0 a ^ n = \begin{cases} a^{n - 1} \cdot a, \ \text{if n is odd}\\ a^{\frac{n}{2}} \cdot a^{\frac{n}{2}}, \text{ if n is even but not 0} \\ 1, \text{if n = 0} \end{cases} an= an1a, if n is odda2na2n, if n is even but not 01,if n = 0

//递归快速幂
int qkpow(int a, int n)
{
    if(n == 0)
    {
        return 1;
    }
    else if(n % 2 == 1)
    {
        return qkpow(a, n - 1) * a;
    }
    else
    {
        int tmp = qkpow(a, n / 2);
        return tmp * tmp;
    }
}

快速幂的优化

在实际问题中,题目常常会要求对一个大素数取模,这是因为计算结果可能会非常巨大,但是在这里考察高精度又没有必要。这时我们的快速幂也应当进行取模,此时应当注意,原则是步步取模,如果MOD较大,还应当开long long。

#define MOD 1000000007;
typedef long long ll;
ll qkpow (ll a, ll n)
{
    if(n == 0)
        return 1;
    else if(n % 2 == 1)
        return qkpow(a, n - 1) * a % MOD;
    else
    {
        ll tmp = qkpow(a, n / 2) % MOD;
        return tmp * tmp % MOD;
    }
}

非递归快速幂

通过将指数写成二进制的形式,可以直接通过判断指数二进制的某一位(位运算)是否为1,来进行进行以上拆分的过程

Example:

7 1 0 7^10 710 10 10 10 转换成改写成十进制形式 ( 1010 ) 2 (1010)_2 (1010)2
那么接下来计算 7 ( 1000 ) 2 ⋅ 7 ( 10 ) 2 7^{(1000)_2} \cdot 7^{(10)_2} 7(1000)27(10)2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GP0g8wHe-1680699079110)(./pic/mi.webp)]

int qpow(int a, int n)
{
    int ans = 1;
    while(n)
    {
        if(n & 1)
            ans *= a;   //位上为1,进行乘法
        a *= a; //确保每一位上乘的是该位的幂
        n>>=1;
    }
    return ans;
}

快速幂的拓展

快速幂是一个模板,任何涉及到计算幂的计算都可以从其中抽象出来

//泛型的非递归快速幂
template <typename T>
T qpow(T a, ll n)
{
    T ans = 1; // 赋值为乘法单位元,可能要根据构造函数修改
    while (n)
    {
        if (n & 1)
            ans = ans * a; // 这里就最好别用自乘了,不然重载完*还要重载*=,有点麻烦。
        n >>= 1;
        a = a * a;
    }
    return ans;
}

应用:矩阵快速幂求解斐波那契数列:

(洛谷P1962)斐波那契数列

已知斐波那契数列的递推关系式为
F n = { 1 ( n ≤ 2 ) F n − 1 + F n − 2 ( n ≥ 3 ) F_n = \begin{cases} 1&(n \leq 2) \\ F_{n - 1} + F_{n - 2} &(n \geq 3) \end{cases} Fn={1Fn1+Fn2(n2)(n3)
F n m o d 1 0 9 + 7 的值 F_n mod 10^9 + 7的值 Fnmod109+7的值

数学模型:

set matrix  A = [ 0 1 1 1 ] , then  A [ F n F n + 1 ] = [ F n + 1 F n + F n + 1 ] then [ F n F n + 1 ] = A [ F n − 1 F n ] = A 2 [ F n − 2 F n − 1 ] = . . . = A n − 1 [ F 1 F 2 ] = A n − 1 [ 1 1 ] \text{set matrix }A = \begin{bmatrix} 0 &1 \\ 1&1 \end{bmatrix}, \text{then } A\begin{bmatrix} F_n \\F_{n+1}\end{bmatrix} = \begin{bmatrix}F_{n+1}\\F_n + F_{n + 1}\end{bmatrix}\\ \text{then} \\ \begin{bmatrix} F_n \\ F_{n + 1} \end{bmatrix} = A\begin{bmatrix}F_{n - 1}\\ F_{n}\end{bmatrix} = A^2\begin{bmatrix}F_{n - 2}\\ F_{n - 1}\end{bmatrix} = ... = A^{n-1}\begin{bmatrix}F_{1}\\ F_{2}\end{bmatrix} = A^{n-1}\begin{bmatrix}1\\ 1\end{bmatrix} set matrix A=[0111],then A[FnFn+1]=[Fn+1Fn+Fn+1]then[FnFn+1]=A[Fn1Fn]=A2[Fn2Fn1]=...=An1[F1F2]=An1[11]

于是斐波那契数列的大小就可以转化成求某个矩阵的幂的问题了。

代码如下:

#include <cstdio>
#define MOD 1000000007
typedef long long ll;

struct matrix
{
    ll a1, a2, b1, b2;
    matrix(ll a1, ll a2, ll b1, ll b2) : a1(a1), a2(a2), b1(b1), b2(b2) {}
    matrix operator*(const matrix &y)
    {
        matrix ans((a1 * y.a1 + a2 * y.b1) % MOD,
                   (a1 * y.a2 + a2 * y.b2) % MOD,
                   (b1 * y.a1 + b2 * y.b1) % MOD,
                   (b1 * y.a2 + b2 * y.b2) % MOD);
        return ans;
    }
};

matrix qpow(matrix a, ll n)
{
    matrix ans(1, 0, 0, 1); //单位矩阵
    while (n)
    {
        if (n & 1)
            ans = ans * a;
        a = a * a;
        n >>= 1;
    }
    return ans;
}

int main()
{
    ll x;
    matrix M(0, 1, 1, 1);
    scanf("%lld", &x);
    matrix ans = qpow(M, x - 1);
    printf("%lld\n", (ans.a1 + ans.a2) % MOD);
    return 0;
}

  • 参考资料:

逆元 —— 广义化的倒数

快速幂

数论-逆元

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值