为什么要Mod 1e9+7
-
为什么要模1000000007(跟我念,一,八个零,七)。
- 大数相乘,大数的排列组合等为什么要取模
- 1000000007是一个质数(素数),对质数取余能最大程度避免结果冲突/重复
- int32位的最大值为2147483647,所以对于int32位来说1000000007足够大。
- int64位的最大值为2^63-1,用最大值模1000000007的结果求平方,不会在int64中溢出。
- 所以在大数相乘问题中,因为(a∗b)%c=((a%c)∗(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出。
- 这道题为什么要取模,取模前后的值不就变了吗?
- 确实:取模前 f(43) = 701408733, f(44) = 1134903170, f(45) = 1836311903, 但是 f(46) > 2147483647结果就溢出了。
- 取模后 f(43) = 701408733, f(44) = 134903163 , f(45) = 836311896, f(46) = 971215059没有溢出。
- 取模之后能够计算更多的情况,如 f(46)
- 这道题的测试答案与取模后的结果一致。
- 总结一下,这道题要模1000000007的根本原因是标准答案模了1000000007。不过大数情况下为了防止溢出,模1000000007是通用做法,原因见第一点。
- 大数相乘,大数的排列组合等为什么要取模
题目1
- 让你求斐波那契数列
思路
- 方法1:一开始我一看就开了个数组记录
- 方法2:然后看别人的做法,发现我没必要开一个数组,因为这道题只要输出一次,所以存最近的两个值就好了
- 方法3:然后看到了别人的又加又减的做法,感觉很interesting,其实思想是一样的
- b = a + b
- a = b - a
- 最后结果是 b
- 性能的话 感觉有点玄学,最下面的是方法1,中间方法2,最上面方法3
- 不知道是不是因为加加减减的原因,方法3的运行时间居然那么长,其实感觉方法1方法2运行时间为0也很奇怪
代码
// solution 2
class Solution {
public:
int fib(int n) {
if(n==0||n==1) return n;
int a, b, tmp;
const int MOD = 1000000007;
a=0,b=1;
for(int i=2;i<=n;i++){
tmp = (a+b)%MOD;
a = b;
b = tmp;
}
return tmp;
}
};
// solution 3
class Solution {
public:
int fib(int n) {
if(n==0||n==1) return n;
int a, b, tmp;
const int MOD = 1000000007;
a=0,b=1;
for(int i=2;i<=n;i++){
b = a + b;
a = b - a;
b = b%MOD;
}
return b;
}
};
题目2
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
- 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
思考
- 找规律,发现是斐波那契数列(毕竟上一题就是斐波那契数列)
- 除此之外,我是这样理解
- (f(i)):第 i 级台阶的跳法数
- (f(i) = f(i-1)+f(i-2))
- 由于只有两种跳跃方法,所以可以是:
- i-1级时的跳法,然后现在跳1级
- i-2级时的跳法,然后现在跳2级
- 注意特殊值,0的时候要是1才行(题目也有要求n=0时的值要是1)
代码
class Solution {
public:
int numWays(int n) {
const int MOD = 1000000007;
if(n==0||n==1) return 1;
int a = 1, b = 1, tmp;
for(int i=2;i<=n;i++){
tmp = (b + a)%MOD;
a = b;
b = tmp;
}
return b;
}
};