1. 大数越界问题概述
大数越界:在程序运算的过程中,变量可能超出
int32
甚至int64
的取值范围。解决方法:
- 循环取余。
- 快速幂取余
- 二分取余
1.1 循环取余
循环取余是最为简单的一种。就是在每次操作时,就对边界进行取余操作,确保结果res,是在边界内的。
把指数操作转换成一次次的乘法,每次相乘就取以此余数,使得数值不超过范围
例如:
1.2 快速幂取余
示例
剑指 offer 16 数值的整数次方(快速幂法),这片里面讲到了如何使用快速幂。
1.2.1 例子代码
int b = n % 3, p = 1000000007;
long rem = 1, x = 3;
for(int a = n / 3 - 1; a > 0; a /= 2) {
if(a % 2 == 1) rem = (rem * x) % p;
x = (x * x) % p;
}
计算a^n % b,其中a,b和n都是32位的整数。例如 2^31 % 3 = 2,例如 100^1000 % 1000 = 0
用快速幂的方法时间复杂度可以达到O(logn)
快速幂思想:x^2 = x * x;x^4 = x^2 * x2;x8 = x^4 * x^4……
快速幂代码
public static int fastPow(int a, int b) {
int ans = 1;
int base = a;
while(b != 0) {
if ((b & 1) != 0) {
ans = ans * base;
}
base *= base;
b >>= 1;
}
return ans;
}
然而这里不是简单的快速幂,还要取模.其实也挺简单,先把计算过程中的变量 ans 和 base 的数据类型改成 long ,防止数值越界,然后就是在计算乘法的时候,再加一步取模
public static int fastPow(int a, int b. int n) {
if (n == 0) { // 规定模 0 是什么
return 1 % b;
}
long ans = 1;
long base = a;
while(b != 0) {
if ((b & 1) != 0) {
ans = (ans * base) % n;
}
base = (base % n) * (base % n);
b >>= 1;
}
return (int)ans;
}
2、%1e9+7和%1000000007的区别
很多力扣题目因为数字过大,要求答案对1000000007(1e9+7)取模,但对1e9+7取模后得到的是浮点型,有时会导致返回值无法类型转换。这时直接对1000000007取模得到int型数据即可。
3、取模公式
( a * b ) % c = [ ( a % c ) * ( b % c ) ] % c
取模运算有这样一个性质:(a+b)%c = ((a%c)+(b%c))%c
所以(pre3+pre2)%1000000007就相当于(pre3%X+pre2%X)%X
用X代替1000000007这样就使得pre3、pre2、pre3+pre2都没有溢出,之后再与pre1相加之后取模,使得全部结果没有溢出
4、为什么会对1e9+7取余?
有时候结果比较大的时候,会对结果进行mod 1e9+7操作。为什么呢?
第一:
1e9+7是一个很大的数,int32位的最大值为2147483647,所以对于int32位来说1000000007足够大。int64位的最大值为2^63-1,对于1000000007来说它的平方不会在int64中溢出所以在大数相乘的时候,因为(a∗b)%c=((a%c)∗(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出 ,否则两个非常大的数在ab阶段就溢出了。有点于归一化的意思。
当一个问题只对答案的正确性有要求,而不在乎答案的数值,可能会需要将取值很大的数通过求余变小。
第二:
其次,1e9+7是一个质数,在模素数p的情况下an(a非p的倍数)的循环节长为p,这是减少冲突的一个原因。另一方面模素数p的环是无零因子环,也就是说两个非p倍数的数相乘再模p不会是零(如果是0的话,在多个数连乘的情况下会大大增加冲突概率)。比如说如果所有的结果都是偶数…你模6就只可能出现0, 2, 4这三种情况…但模5还是可以出现2, 4, 1, 3这四(4=5-1)种情况的… hash表如果是用取模的方法也要模一个大质数来减少冲突,出题人也会这样来 希望减少你“蒙对“的概率。
面试题:
字节笔试题,求大数对(1e9+7)值取模结果_易_的博客-CSDN博客_2的多少次方大于1e9+7
参考资料: