快速幂与模运算
快速幂与模运算
模运算
讲解
相信大家已经不少见到模运算了,但是基本上都是在比赛打出来的,没有系统地讲,今天我带着大家一起梳理一下。
定义模运算:即 \(a\) 除以 \(b\) 的余数,形式化地:\(a\bmod b=a-\lfloor b\times\frac{a}{b}\rfloor\),其中 \(\lfloor x\rfloor\) 表示对 \(x\) 向下取整。
根据定义,有:\(0\leq a\bmod b \leq b-1.\)
对于 C++语言
,求 \(a\bmod b(a < 0)\) 有问题。
举个例子,对于 \(-5\bmod 2\) 在其中为 \(-2\) 而不是 \(-3\),这是因为 C++
中规定的整型除法向 \(0\) 取整,换句话讲,就是取精度最靠近 \(0\) 的那一个整数,具体地,代码如下:
int a = -5;
int b = 2;
int c = a / b;
cout << c << endl;//输出:-2
我们不难发现并且不难证明以下的式子:
- \((a + b)\bmod p=(a\bmod p + b\bmod p)\bmod p.\)
- \((a - b)\bmod p=(a\bmod p - b\bmod p)\bmod p.\)
- \((a \times b)\bmod p=(a\bmod p \times b\bmod p)\bmod p.\)
有同学发现了,为什么没有除法,没有关系,因为其式子形式不同于上面的式子,当讲到 逆元
的时候便知道了。
并且注意到上面式子的第二个,在编写程序的时候不妨在里面多加几个 \(p\),使得其为正数。
于是便有了一道题目。
例题
给出 \(3\) 个数 \(a,b,p\),求出 \(a\times b\bmod p\) 的值,其中 \(1\leq a,b,p\leq 10^{18}.\)
不难有以下代码:
int main(){
int a,b,p;
cin >> a >> b >> p;
cout << (a % p) * (b % p) % p;
return 0;
}
有一个测试点:
输入:
999999999999999999 999999999999999999 1000000000000000000
显然地会让整个运算过程超出 long long
的范围。于是有了以下算法。
慢速乘
分治思想。
我们不难将 \(a\times b\) 拆分成下面的式子:
\(a\times b = \frac{a}{2}\times b\times 2\)
现在化为求 \(\frac{a}{2}\times b\),最后的结果再乘上 \(2\) 就行了,不断地分治时间复杂度就到了 \(\mathcal{O}(\log a).\)
有了以下的代码:
#include <iostream>
#include <cstdio>
#idefine int long long
int qtimes(int a,int b,int mod) {
a %= mod,b %= mod;
int res = 0;
while(b) {
if (b & 1) res = (res + a) % mod;
a = a * 2 % mod;
b >>= 1;
}
return res;
}
signed main() {
int a,b,mod;
cout << (a % mod) * (b % mod) % mod << endl << qtimes(a,b,mod) << endl;
}
以上的程序可以对照两者的答案为什么不一样。
快速幂
其实和上面同理可得出 \(a^b\) 的幂是可加的,我们不难得到 \(\left(a^{\frac{b}{2}}\right)^2.\)
通过不断分治,不难得到以下的代码:
int qpow(int a,int b) {
int res = 1;
while(b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}