快速幂与快速幂求逆元

目录

  • 取模运算法则
  • 快速幂
    • 快速幂的定义
    • 快速幂的计算步骤
    • 典型例题
    • 代码实现
    • 扩展运用
  • 快速幂求逆元
    • 逆元的定义及计算
    • 典型例题
    • 代码实现


取模运算法则

序号 取模概念下的加减乘除 正确性
1 ( a + b ) (a+b) (a+b) % p = ( a p=(a p=(a % p + b p+b p+b % p ) p) p) % p p p 正确
2 ( a − b ) (a-b) (ab) % p = ( a p=(a p=(a % p − b p-b pb % p ) p) p) % p p p 正确
3 ( a ∗ b ) (a*b) (ab) % p = [ ( a p=[(a p=[(a % p ) ∗ ( b p)*(b p)(b % p ) ] p)] p)] % p p p 正确
4 ( a / b ) (a / b) (a/b) % p = [ ( a p=[(a p=[(a % p ) / ( b p) / (b p)/(b % p ) ] p)] p)] % p p p 错误

快速幂

快速幂的定义

快速幂(Fast Power)是一种高效计算乘方的方法,主要用于计算大整数的大幂次方。其基本思想是利用分治思想和乘方的对数规律,将运算复杂度从 O ( n ) O(n) O(n) 降低到 O ( log ⁡ n ) O(\log n) O(logn)

a k = a 2 x 1 ∗ a 2 x 2 ∗ a 2 x 3 ∗ . . . = a 2 x 1 + 2 x 2 + 2 x 3 + . . . a^k = a^{2^{x_1}} * a^{2^{x_2}} * a^{2^{x_3}} * ... = a^{2^{x_1} + 2^{x_2} + 2^{x_3} + ...} ak=a2x1a2x2a2x3...=a2x1+2x2+2x3+...


快速幂的计算步骤

  1. 将指数进行二进制拆分。例如计算 a 1 3 a^13 a13,13的二进制为 1101 1101 1101,可以拆分为 1 + 4 + 8 1 + 4 + 8 1+4+8
  2. 根据拆分后的指数,将原问题拆分为多个子问题: a 1 , a 4 [ 即 ( a 2 ) 2 ] , a 8 即 [ ( a 4 ) 2 ] a^1, a^4[即(a^2)^2], a^8即[(a^4)^2] a1,a4[(a2)2],a8[(a4)2]
  3. 从最低位开始,逐次计算所有的子问题,并依次平方就可以得到最终结果。
  4. 在计算过程中记录中间结果,避免重复计算。

典型例题

题目描述:
给定 n n n a i , b i , p i a_i,b_i,p_i ai,bi,pi,对于每组数据,求出 a i b i m o d    p i a_i^{b_i} \mod p_i aibimodpi 的值。

输入格式:
第一行包含整数 n n n

接下来 n n n 行,每行包含三个整数 a i , b i , p i a_i,b_i,p_i ai,bi,pi

输出格式:
对于每组数据,输出一个结果,表示 a i b i m o d    p i a_i^{b_i} \mod p_i aibimodpi 的值。

每个结果占一行。

数据范围:
1 ≤ n ≤ 100000 , 1 ≤ a i , b i , p i ≤ 2 × 1 0 9 1≤n≤100000,1≤a_i,b_i,p_i≤2×10^9 1n100000,1ai,bi,pi2×109

输入样例:

2
3 2 5
4 3 9

输出样例:

4
1

代码实现

模运算的性质: ( a ∗ b ) (a * b) (ab) % p = ( a p = (a p=(a % p ∗ b p * b pb % p ) p) p) % p p p

#define _CRT_NO_SECURE_WARNINGS
#include

using namespace std;

int qmi(int a, int b, int p)
{
	long long res = 1 % p; // 防止 p=1 的情况
	while (b)
	{
		if (b & 1) res = res * a % p;
		a = a * (long long)a % p;
		b >>= 1;
	}
	return res;
}
int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a, b, p;
		cin >> a >> b >> p;
		cout << qmi(a, b, p) << endl;
	}
	return 0;
}

扩展运用

题目描述:
64 64 64位整数乘法)求 a a a b b b p p p 取模的值。

输入格式:
第一行输入整数 a a a,第二行输入整数 b b b,第三行输入整数 p p p

输出格式:
输出一个整数,表示 a ∗ b m o d    p a∗b \mod p abmodp的值。

数据范围:
1 ≤ a , b , p ≤ 1 0 18 1≤a,b,p≤10^{18} 1a,b,p1018

输入样例:

3
4
5

输出样例:

2

思路:
二进制思想。如果直接计算 a × b a×b a×b 这会爆 long long ,所以采用 类似于快速幂的思想 b b b 作为二进制形式进行处理,然后如果某位上为 1 1 1 就加上它 a × 2 a×2 a×2 对应的次方,并且每次计算后取模就可以了。

例如: b = 11 = ( 1011 ) 2 = 23 + 21 + 20 b=11=(1011)_2=23+21+20 b=11=(1011)2=23+21+20,那么 a × b = a × ( 2 3 + 2 1 + 2 0 ) = 8 a + 2 a + a a×b=a×(2^3+2^1+2^0)=8a+2a+a a×b=a×(23+21+20)=8a+2a+a

代码实现:

#define _CRT_NO_SECURE_WARNINGS
#include
using namespace std;

long long mul(int a, int b, int p)
{
	long long res = 0;
	while (b)
	{
		if (b & 1) res = (res + a) % p;
		a = (a % p + a % p) % p;
		b >>= 1;
	}
	return res;
}
int main()
{
	long long a, b, p;
	cin >> a >> b >> p;
	cout << mul(a, b, p) << endl;
	return 0;
}

快速幂求逆元

逆元的定义及计算

若整数 b , m b,m b,m 互质,并且对于任意的整数 a a a,如果满足 b ∣ a b|a ba,则存在一个整数 x x x,使得 a b ≡ a × x ( m o d    m ) \frac{a}{b} ≡ a × x (\mod m) baa×x(modm),则称 x x x b b b 的模 m m m 乘法逆元,记为 b − 1 ( m o d    m ) b^{-1} (\mod m) b1(modm)

b b b 存在乘法逆元的充要条件是 b b b 与模数 m m m 互质。

同时可以写作为 b ∗ b − 1 ≡ 1 ( m o d    m ) b*b^{-1} ≡ 1(\mod m) bb11(modm)

当模数 m m m 为质数时, b m − 2 b^{m-2} bm2 即为 b b b 的乘法逆元。由欧拉定理 a φ ( m ) ≡ 1 ( m o d    m ) a^{φ(m)} ≡1(\mod m) aφ(m)1(modm),当 m m m 为质数时, φ ( m ) = m − 1 φ(m) = m - 1 φ(m)=m1,则可以配出 b ∗ b m − 2 ≡ 1 ( m o d    m ) b*b^{m-2} ≡ 1(\mod m) bbm21(modm) 的表达式,即 b b b 的乘法逆元为 b m − 2 b^{m-2} bm2


典型例题

题目描述:
给定 n n n a i , p i a_i,p_i ai,pi,其中 p i p_i pi 是质数,求 a i a_i ai p i p_i pi 的乘法逆元,若逆元不存在则输出 impossible

输入格式:
第一行包含整数 n n n

接下来 n n n 行,每行包含一个数组 a i , p i a_i,p_i ai,pi,数据保证 p i p_i pi 是质数。

输出格式:
输出共 n n n 行,每组数据输出一个结果,每个结果占一行。

a i a_i ai p i p_i pi 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible

数据范围:
1 ≤ n ≤ 1 0 5 , 1 ≤ a i , p i ≤ 2 × 1 0 9 1≤n≤10^5,1≤a_i,p_i≤2×10^9 1n105,1ai,pi2×109

输入样例:

3
4 3
8 5
6 3

输出样例:

1
2
impossible

代码实现

#define _CRT_NO_SECURE_WARNINGS
#include
using namespace std;
int qmi(int a, int b, int p)
{
	int res = 1 % p;
	while (b)
	{
		if (b & 1) res = res * a % p;
		a = a * (long long)a % p;
		b >>= 1;
	}
	return res;
}
int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a, p;
		cin >> a >> p;
		int res = qmi(a, p - 2, p);
		if (res % p == 0) cout << "impossible" << endl;
		else cout << res << endl;
	}
	return 0;
}

你可能感兴趣的:(从零开始的算法打灰,算法,c++)