模运算与幂运算

来源:互联网 发布:dede源码素材资源网 编辑:程序博客网 时间:2024/05/06 13:08
模运算与幂运算
         你需要花多少时间做下面这道题:
    123456789 * 987654321 = (      )
      A. 121932631112635266
      B. 121932631112635267
      C. 121932631112635268
      C. 121932631112635269
    既然是选择题,不必费力把答案完整地计算出来——4个选项的各位都不相同,因此只需要计算出答案的最后一位即可。不难看出:1 * 9 = 9。刚才的解题过程抽象出来就是下面的式子了:
123456789×987654321 mod 10 = (123456789 mod 10) × (987654321 mod 10) mod 10
其中a mod b表示a除以b的余数,C++语言是a % b。
不难得出下面的公式:
    (a + b) mod p = ((a mod p) + (b mod p)) mod p
    (a - b) mod p = ((a mod p) - (b mod p)) mod p
    (a×b) mod p = ((a mod p) × (b mod p)) mod p
    (ab) mod p = ((a mod p)b ) mod p

【程序1】大整数取模 
    输入正整数n和m,输出n mod m的值。n<=10100,m<=108

【分析】利用上面的公式每步取模即可。例如n=123,m=7。则123 mod 7=((((1 mod 7)*10 + 2) mod 7)*10 + 3) mod 7 = 4:
#include <iostream>using namespace std;int main(){string n;int m;cin >> n >> m;int ans=0;for(int i=0;i<n.size();i++) ans = (int) (((long long )ans*10 + n[i]-48) % m);cout << ans;return 0;}
        
    有一类常见的运算,就是求形如ab mod p的值,一般情况下,我们可以用幂的定义来作:通过乘法的倍乘来实现,需要做b次乘法和模运算。如果b较大时,这样的方法就不行了,下面的程序采用二分法进行优化。

【程序2】幂取模 
    输入正整数a、n和m,输出an mod m的值。a,n,m<=109

【分析】本题采用二分法。本题的数据很大,必须时刻运用上面的公式:
    a*b*c mod k = (((a mod k)*(b mod k) mod k) *(c mod k)) mod k
#include <iostream>using namespace std;long long pow_mod(long long a, long long n, long long m){if(n==1) return a%m;long long x=pow_mod(a,n/2,m);   if (n%2 == 1) return (((x % m)*(x % m)) % m *(a % m)) % m;  else return ((x % m)*(x % m)) % m;}int main(){long long a,n,m;cin >> a >> n >> m;cout << pow_mod(a,n,m) << endl;return 0;}
        
    上面的程序,对于b比较大时,还是无法通过的。那么我们可以采用二进制优化:
    我们把b按二进制位展开,例如:
    b=5,则5=20+22,得到a5=a20 + 22。也就是说,如果b可以表示成b=2b0+2b1+...+2bk的形式,那么ab可表示成:
        ab = a2b0 + 2b1 + ... + 2bk = a2b0 × a2b1 × ... × a2bk
         程序2“幂取模”的核心程序如下:
long long calc(int a, int b, int m){  //函数计算a^b mod m的值    int  b1[32],r[32];  //b1存储b的二进制结果,r[i]表示2^i次方对m取模的结果    r[0]= a % m;  //计算r[i]    for (int i=1;i<32;++i) r[i]=(long)r[i-1]*r[i-1] % m;  //r^(i+1) = (r^i) * (r^i)    i=0;    while (b){b1[i++] = b % 2;b>>=1;    }    for (int j=0,k=1; j<i; ++j)  //计算乘方      if(b1[j]) k=k*r[j]%m;    return k;}int main(){    scanf("%d%d%d",&a,&b,&m);    printf("%u\n",calc(a,b,m));}

【程序3】高级模运算
        人与人是不同的,有些人喜欢阅读杂志,有些人喜欢下棋,有的人喜欢麻烦的数字游戏。比如ESSE论坛的一次活动:
    每个人选择两个数字ai和bi写在纸上,其他人不能看见。过了一段时间后,每个人说出自己纸上的数字,然后每个人的目标就是求出所有的 aibi的和模m的值,最先算出结果的,就是胜利者。
输入
    输入包含Z组测试数据,第1行只包含数字Z。随后是测试数据:第1行是一个数字M(1<=M<=45 000)。第2行是数字H(1<=H<=45 000)表示参加游戏的人数。接下来H行,每行两个数ai和bi(1<=ai, bi<=231)。
输出
    对于每组测试数据,输出以下表达式的值:
        (a1b1 + a2b2 + ... + aHbH) mod M
输入样例
3
16
4
2 3
3 4
4 5
5 6
36123
1
2374859 3029382
17
1
318132
输入样例
2
13195
13
【分析】利用上面的模运算公式,表达式的任何中间结果都可以进行取模运算,使得中间结果不至于太大而溢出。本题采用二进制优化。


【程序4】模线性方程
        输入正整数a,b,n,解方程 ax≡b(mod n)。 a,b,n≤10^9。

本题中出现了一个新记号:同余。a≡b(mod n)的含义是“a和b关于模n同余”,即 a mod n = b mod n
不难看出:a≡b(mod n)的充要条件是: a-b是n的整数倍。
a≡b(mod n)的充要条件是 a-b是n的整数倍。
         这样,原来的方程就可以理解成:ax-b是n的整数倍。设这个倍数为k,则ax-b=nk,移项得:ax-nk=b。这恰好就是前面介绍的不定方程,不再讨论,具体参看“3-1 最大公约数和最小公倍数”。
方程ax≡1(mod n)的解称为a关于模n的逆。当gcd(a,n)=1时,该方程有唯一解;否则无解。
0 1
原创粉丝点击