模运算(modulus operation)

本文详细介绍了C++中乘法取模运算的原理,指出直接计算可能出现溢出问题,并提供了两种解决方案:一种通过连续操作将乘积缩小,另一种是将数字转换为字符串进行处理。最后提到了Python处理类似问题的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

模运算

C++乘法取模

第1种

第2种


模运算

模运算是大数运算中的常用操作。作为一个数太大,无法直接输出,或者不需要直接输出,可以把它取模后缩小数值再输出。因为模运算有缩小数值范围的功能,所以常用于哈希计算。

定义取模运算为求a除以m的余数,记为a mod m = a % m。

正整数取模运算的结果满足0≤a mod m≤m-1,其含义是用给定的m限制计算结果的范围。例如m=10,就是取其计算结果的个位数。

一般求余都是正整数的操作,如果是对负整数求余,不同编程语言的结果可能不同,例如
C++、C和java:5%3输出2,(-5)%(-3)输出-2,(-5)%3输出-2,5%(-3)输出2。计算规则为先按正整数求余,然后加上符号,符号与被除数一致。
Python:5%3输出2,(-5)%(-3)输出-2,(-5)%3输出1,5%(-3)输出-1。因为Python的求余是向下取整。

取模操作满足以下性质:
(1)加。(a + b) mod m = ((a mod m) + (b mod m)) mod m。如果没限制正负,那么C、C++、Java等语言实现的等式左右可能会符号相反,大小相差一个m;但是Python不存在这个问题。
(2)减。将(1)中的b换成-b。
(3)乘。(a * b) mod m = ((a mod m) * (b mod m)) mod m。
注意:(a / b) mod m = ((a mod m) / (b mod m)) mod m是错误的

C++乘法取模

C++对于乘法取模运算代码需要注意。两个大数a,b做乘法取模时,直接用(a * b) mod m = ((a mod m) * (b mod m)) mod m可能出错,因为a*b可能溢出,(a mod m) * (b mod m)也可能溢出。

其做法可以如下,以下代码在一定程度上能得到正确答案(均是m不会特别大时):

第1种

原理如下:

为了不直接计算a*b,改为计算(a*2)*(b/2),其中a*2不会溢出,b/2不会溢出。连续执行此操作,直到b为0。
(1)b为奇数。此时a*b=(a*2)*(b/2)+(a*2);
(2)b为偶数。此时a*b=(a*2)*(b/2)。

此时m小于a和b可以用,如果m大于a和b,那么也会溢出,因为当a为最大值时,a + a此时就已经溢出。此时应该如何呢?

typedef long long ll;
ll mul(ll a, ll b, ll m) {//乘法取模:a*b mod m
	a = a % m;//防止a+a溢出
	b = b % m;
	ll ans = 0;
	while (b) {
		if (b & 1)ans = (ans + a) % m;//用b&1判断奇数偶数
		a = (a + a) % m;//保证a+a不能溢出
		b >>= 1;
	}
	return ans;
}

第2种

原理如下:

将输入的两个数当成字符串,然后申请两个能够容纳两个字符串长度的数组,将其进行逆序存放,然后在申请一个能够容纳两个数据相乘之后的结果数组,这里要注意,结果数组长度最大不会草超过两个数据长度之和,因此在申请内存的时候,我们尽量不要申请太多(节约运算时间),结果数据中的值满足以下关系:
result[i+j]=data_a[i] * data_b[j];
再利用除法的运算过程实现除余功能。

typedef long long ll;
ll mul(string a, string b, ll m) {
	int a_len = a.length();
	int b_len = b.length();
	int i = 0; int j = 0; //开始申请存储a,b的数组
	int* data1 = (int*)malloc(a_len * sizeof(a_len));
	int* data2 = (int*)malloc(b_len * sizeof(b_len));//申请存放结果的数组,两数相乘,位数最多为a_len + b_len
	int* result = (int*)malloc((a_len + b_len) * sizeof(a_len + b_len));
	memset(data1, 0, a_len * sizeof(a_len));
	memset(data2, 0, b_len * sizeof(b_len)); 
	memset(result, 0, (a_len + b_len) * sizeof(a_len + b_len));
	for (i = a_len - 1, j = 0; i >= 0; i--, j++) data1[i] = a[j] - '0';//开始存放a和b,注意这里是倒叙存放
	for (i = b_len - 1, j = 0; i >= 0; i--, j++) data2[i] = b[j] - '0';
	for (i = 0; i < a_len; i++) {//开始进行相加,结果存放在result中
		for (j = 0; j < b_len; j++) result[i + j] += data1[i] * data2[j];
	}
	for (i = 0; i < a_len + b_len; i++) {//开始处理进位
		if (result[i] > 9) result[i + 1] += result[i] / 10;
		result[i] = result[i] % 10;
	}
	int index = 0;
	for (i = a_len + b_len - 1; i >= 0; i--) {//开始将结果合并 , 这里要倒叙合并
		if (result[i] == 0) continue;
		else {
			index = i;
			break;
		}
	}
	ll ans = 0;
	for (i = index; i >= 0; i--) {
		ans = (ans * 10 + result[i]) % m;
	}
	return ans;
}

这个代码同样ans*10+result[i]会有可能越界

解决此问题可以采用Python解决,因为Python没有溢出问题。

代码如下:

a, b, m = input().split()
a = int(a)
b = int(b)
m = int(m)
print((a * b ) % m)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayNe61

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值