同余与模运算

a mod b 表示 a 除以 b 的余数,在高级语言中表示成 a % b

int mod(int a,int b)
{
    return a%b;
}

我们先记住下面几个公式:

  • 1. (a+b) mod n=((a mod n)+(b mod n)) mod n
int add_mod(int a, int b, int n)
{
    a %= n; b %= n;
    return (int) ((long long)(a + b) % n); 
}
  • 2.(ab) mod n=((a mod n)(b mod n)+n) mod n
    注意这里的 a mod n 可能会小于 b mod n ,则需要在结果上加上 n

    int minus_mod(int a, int b, int n)
    {
        a %= n; b %= n;
        return (int) ((long long)(a - b + n) % n); 
    }
    • 3. ab mod n=(a mod n)(b mod n) mod n
      注意这里的 a mod n b mod n 的乘积可能会溢出
    int mul_mod(int a, int b, int n)
    {
        a %= n; b %= n;
        return (int) ((long long)a * b % n); 
    }

    大整数取模:

    计算一下222222222222222222222222 mod 1234的结果,是否可以用下面的程序计算?

    int mod(long long a, long long b)
    {
        return a%b;
    }

    它计算出来的结果是 441,可惜正确答案是1036
    显然,我们能发现其中的错误在哪,因为long long的范围是9223372036854775807,所以一旦 a 大于long long的范围,那就肯定是错的。所以我们不能用整型来保存整数,要用字符数组来保存。

    分析:

    首先将大整数写成”自左向右“的形式,比如 1234=((110+2)10+3)10+4 ,然后再用上面的公式1进行每步取模。
    代码如下:

    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    int main()
    {
        char n[100];
        int m;
        scanf("%s%d",n,&m);
        int len = strlen(n);
        int ans = 0;
        for(int i=0; i<len; i++)
        {
            ans = (int)(((long long)ans*10 + n[i] - '0') % m);
        }
        printf("%d\n",ans);
        return 0;
    }

    这样再大的整数都没有问题。


    幂取模:

    如何计算出 abmodn 的值
    我们可以使用上面的公式3,进行每步取模,因为 ab=aaaaaa...a 共有b个a相乘,每一次 ans = ans * a%n 即可。

        int pow_mod(int a, int b, int n)
        {   
            int ans = 1;
            for(int i=0; i<b; i++)
            {
                ans = (int)((long long)ans * a % n);
            }
            return ans;
        }

    这里的时间复杂度就是 O(n) 了,但是如果b很大呢,所以我们需要优化一下:

    这里采用二分法的思想。
    比如 a14=(a7)2a7=(a3)2aa3=a2a
    这样代码就是:

    #include<cstdio>
    using namespace std;
    
    /*时间复杂度为O(logn)*/
    int pow_mod(int a, int b, int n)
    {
        if(b == 0) return 1;
        int x = pow_mod(a, b/2, n);
        long long ans = (long long)x * x % n;
        if(b%2 == 1) ans = ans * a % n;
        return (int) ans;
    } 
    
    int main()
    {
        int a,b,n;
        scanf("%d%d%d",&a,&b,&n);
        printf("%d\n",pow_mod(a,b,n));
        return 0;
    } 

    时间复杂度变成了 O(logn) 了。


    模线性方程组

    问题描述:输入正整数 a,b,n ,解方程 axb(mod n) a,b,n109

    ”这个记号叫做同余ab(mod n) 的含义是“ a b 关于模 n 同余”,即 a mod n=b mod n 。所以我们可以推出 ab(mod n) 的充要条件是: ab n 的整数倍。

    这样,原方程 axb(mod n) ,可以理解为: axb n 的整数倍。设倍数为 y,则 axb=ny ,移项得 axny=b 。通过扩展欧几里得算法解出此方程。

    特殊情况:
    b=1 时, ax1(mod n) 的解称为 a 关于模 n。所以根据上面的分析, axny=1 要有解,根据扩展欧几里得算法,1必须是 gcd(a,n) 的倍数,也就是说 gcd(a,n)=1 ,则 a n 必须互素才会有解,并且只有唯一解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值