c++求模运算的应用

在 C\C++ 中,% 符号为求模运算符,即 a%b 表示 a 除以 b 的余数。

周期问题

我们在奥数中经常遇到这样的问题:

  1. 给你一串数字 4 5 6 4 5 6 4 5 6 ...,求这里的第 1000 个数字是几?

我们发现这是一个周期数列,周期为 3。

a. 如果将4看作第 1 个数,第 1000 个数一定是周期中的第 1 个数 4,因为前面 999 个数刚好是 333 组 456 构成的循环。

b. 如果将4看作第 0 个数,那么第 1000 个数此时就对应为第 999 个数,因为 999 模 3 余数为 0,同样对应的是 4。

  1. 给你一串数字 0 1 2 3 4 5 6 4 5 6 4 5 6...,求这里的第 1000 个数字是几?

这还是一个周期数列,周期为 3,但是它是从第 5 个数开始循环的,因此我们应该用 (1000-4) % 3,算出来是 0,这该怎么办呢?

针对这个问题,我们可以有两种处理方法:

(1)特殊处理:遇到 0 时,取周期中的最后一个数 6;

(2)统一处理:我们不要从 1 开始数,由于 % 运算会出现 0,因此我们就从 0 开始数。我们将第一个循环的数字 4 (目前是第 5 个数)变成第 0 个数,重新写算式 (1000-5) % 3,结果为 2,则 456 中,4 作为周期的第 0 个数,那么 6 就是第 2 个数。

练习题: 凯撒密码icon-default.png?t=N3I4https://www.mfstem.org/p/368

这一题里,每个字母都被变成它后面的第 3 个字母,因此直接将其 ASCII 码值加 3 即可。

但是,最后 3 个字母却遇到了问题,x→a,y→b,z→c,这个不是加 3 能够实现的。如何解决呢?

我们可以想象一下,把 26 个字母看成上面那样的循环:a,b,c,d,…,z,a,b,c,d,…,这样的话,当我们遇到x→a 的问题时,x 是第 23 个字母(从 0 开始数的),就相当于求第 26 个字母(计算23+3得到)是什么。那很容易想到,第 26 个字母就是 a。

 那么现在还有一个问题,就是在 C\C++ 中,'a' 是用 ASCII 值 97 存储的,其余字母的 ASCII 值依次是 98,99,…98,99,…,我们需要先将 'a' 看成第 0 个字母,因此,如果我们要对一个字母变量 alpha 进行凯撒加密,计算其后面第 3 个字母时,需要先执行 alpha-'a',然后再加 3,除以 26 取模,完整的算式应该是 (alpha - 'a' + 3) % 26 + 'a'

#include <iostream>
using namespace std;
int main() {
    char alpha;
    cin >> alpha;
    //这里 'a' 就是97,尽量不要写 97,而是直接用 'a' 表示,这样可读性更强。
    alpha = (alpha - 'a' + 3) % 26 + 'a';
    cout << alpha;
    return 0;
}

获取一个整数的个位、十位

我们可以通过 a%10 来求得整数 a 的个位,通过 a/10%10 来获得它的十位。

所以,我们要获取一个位,需要分两步:

(1)将这个位用除以一次或多次 10 的方法变成个位;

(2)用 %10 的方法获取现在的这个个位即可。

例如,659 % 10 得到个位的 9; 659/10%10 得到十位的 5;659/10/10%10 得到百位的 6。虽然可以通过 /100 直接获得 6,但是如果遇到更多位数的时候,我们后面会通过循环来实现这个操作,那么每次循环就只除以 10,即可依次获取从个位到最高位的所有位。

要获得某一位的值,就先把这个位通过除以 10 的整数次方,移动到个位,再模 10 即可。

求模运算的公式

​(1).(a+b+c)%M=(a%M+b%M+C%M)%M

(2).(a−b−c)%M=((a%M−b%M−C%M)%M+M)%M

(3).(a∗b∗c)%M=a%M∗b%M∗C%M

快速幂

#include <iostream>
typedef long long LL;
LL qpow(LL a, int b, int M) {
    LL ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % M;
        a = a * a % M, b >>= 1;
    }
    return ans;
}
int main() {
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    printf("%lld", qpow(a, b, c));
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值