把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

快速幂与模运算

快速幂与模运算

模运算

讲解

相信大家已经不少见到模运算了,但是基本上都是在比赛打出来的,没有系统地讲,今天我带着大家一起梳理一下。

定义模运算:即 \(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;
}
posted @ 2024-10-31 12:44  high_skyy  阅读(7)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end