目录
模运算
模运算是大数运算中的常用操作。作为一个数太大,无法直接输出,或者不需要直接输出,可以把它取模后缩小数值再输出。因为模运算有缩小数值范围的功能,所以常用于哈希计算。
定义取模运算为求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)