模运算与取余——数轴上绕圈子的学问
中等3模运算与取余——数轴上绕圈子的学问
你有没有想过,时钟为什么是12小时制?如果现在是11点,再过3小时,不是11+3=14点,而是14 mod 12 = 2点,也就是下午2点。这个“mod”就是取余运算,在C++中用百分号 % 表示。简单说,取余就是求一个数除以另一个数后剩下的“零头”。
模运算(取余)在编程里超级有用:判断奇偶、控制循环、处理大数、解决日常问题(比如分零食、算星期几)。理解它,就像在数轴上“绕圈子”——每次加一圈,又回到起点。
1. 什么是取余?生活中的“绕圈子”
取余 a % b 计算的是 a 除以 b 的余数。规则是:余数必须小于除数,且大于等于0(正数情况下)。
生活中的例子:
- 分糖果:你有14颗糖,要平均分给3个小朋友,每人分4颗,剩下2颗。14 % 3 = 2。
- 排队:你们班40人,每7人排一队,最后一队有多少人?40 % 7 = 5,最后5人单独一队。
- 星期几:今天是星期一,过了10天是星期几?(1 + 10) % 7 = 4,星期四(星期一算1,星期二2,……星期日0或7,这里约定星期一到日用1
7,但更常见的是06)。
这些例子都说明取余就是“绕圈子”,结果总是落在0到(除数-1)之间。
2. C++中的 % 运算符
在C++中,% 只能用于整数(int、long、long long等)。用法和加减乘除一样,但要注意:除数不能为0,否则程序崩溃。
语法:
int result = a % b; // 变量a除以变量b的余数
常见场景:取个位数
int num = 12345;
int ge = num % 10; // ge = 5,相当于切掉十位以上,只留个位
关于负数:
初学阶段我们只处理正数。如果你遇到负数,C++的 % 结果可能为负(比如 -5 % 3 在C++中可能是 -2,数学上更习惯取正余数1)。不过别担心,CSP-J题目通常明确说“输入非负整数”,我们只关心正数情况。
3. 模运算的常见用途
✅ 判断奇数偶数
任何整数 n,如果 n % 2 == 0 就是偶数,否则是奇数。
int n = 7;
if (n % 2 == 0)
cout << "偶数" << endl;
else
cout << "奇数" << endl;
✅ 判断闰年
闰年规则:能被4整除但不能被100整除,或者能被400整除。
int year = 2024;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
cout << year << "是闰年" << endl;
✅ 循环节控制(比如星期几、数组下标轮转)
假设有一个长度为3的数组,我们想循环访问元素:下标0→1→2→0→1→2……可以用 index = (index + 1) % 3 实现。
int index = 0;
index = (index + 1) % 3; // index变成1
index = (index + 1) % 3; // index变成2
index = (index + 1) % 3; // index变成0,回到起点
✅ 大数防溢出(取模运算)
当数字很大时,比如计算阶乘或斐波那契数列,结果会超过int范围。我们经常只关心结果对某个数取模,比如模 10007。
int fib[100];
fib[0] = 0; fib[1] = 1;
for (int i = 2; i < 100; i++)
fib[i] = (fib[i-1] + fib[i-2]) % 10007; // 只保留余数,防止溢出
✅ 判断整除
如果 a % b == 0,说明 a 能被 b 整除。比如判断一个数是不是3的倍数:if (n % 3 == 0)。
4. 新手容易犯的常见错误
❌ 错误1:除数写成0
int a = 10, b = 0;
int c = a % b; // 运行时错误!程序崩溃
正确做法:写代码时确保除数不为0,或者先判断再计算。
❌ 错误2:对浮点数用 %
double x = 5.5;
int y = x % 2; // 编译错误:%不能用于浮点数
正确做法:先把浮点数转换成整数(比如强制类型转换),或者用数学函数 fmod (C++标准库提供)。
❌ 错误3:忘记运算符优先级
% 和 *、/ 是同级的,从左到右计算。比如:
int ans = 10 + 20 % 3; // 先算20%3=2,再算10+2=12,不是(10+20)%3=0
正确做法:如果不确定,就加括号 (10+20)%3。
❌ 错误4:误解取余范围
正数取余结果在 0 到 b-1 之间,但很多人以为结果可以是负数。C++中负数取余可能为负(如 -5 % 3 = -2),建议初学只用正数运算。
5. 完整示例:计算未来是星期几
假设今天是星期一(我们把星期一当作1,星期二2,……星期日7)。输入过几天,输出那天是星期几。
#include <iostream>
using namespace std;
int main() {
int today = 1; // 今天是星期一
int days; // 过几天
cout << "请输入天数: ";
cin >> days;
// 注意:星期几是1~7,不能用days%7直接得结果
// 计算方法:(today + days - 1) % 7 + 1
// 减1再+1是为了把余数0~6映射到1~7
int future = (today + days - 1) % 7 + 1;
cout << "过了" << days << "天后是星期" << future << endl;
// 保持原有内容:奇偶判断
int n;
cout << "再输入一个整数,判断奇偶: ";
cin >> n;
if (n % 2 == 0)
cout << n << "是偶数" << endl;
else
cout << n << "是奇数" << endl;
// 取个位数
cout << "个位数是: " << n % 10 << endl;
// 维持原有闰年判断
int year = 2024;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
cout << year << "是闰年" << endl;
else
cout << year << "不是闰年" << endl;
return 0;
}
运行结果示例:
请输入天数: 10
过了10天后是星期4
再输入一个整数,判断奇偶: 7
7是奇数
个位数是: 7
2024是闰年
6. 相关知识点指引
学完取余,你可以继续探索这些有意思的内容:
- 快速幂与模运算:计算大数幂(比如
2^100 mod 1007),避免直接乘爆。 - 同余方程:判断两个数模某个数相等,例如
a ≡ b (mod m)。 - 最大公约数与模逆元:用辗转相除法求gcd,以及模运算下的“除法”。
- 循环队列:用取余实现数组的环形使用,像游戏中的轮换。
记住:取余就像在数轴上绕圈子——每次加一圈,你又回到了熟悉的地方。多动手写代码,很快就能掌握!
例题精讲
在C++中,表达式 (-7) % 3 的结果是?
若 a % b == 0 成立(b≠0),则 a 一定能被 b 整除。
以下程序输入一个正整数 n,输出 n 除以 3 的余数。请补全代码。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
cout << ___ << endl;
return 0;
}已知 m 为正整数,下列哪个表达式在C++中恒成立(对所有整数 a, b)?
以下程序读入 n 个整数,输出它们之和除以 100 的余数。请补全循环体中的语句。
#include <iostream>
using namespace std;
int main() {
int n, x, sum = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> x;
___;
}
cout << sum % 100 << endl;
return 0;
}