取模运算的应用
LeetCode里遇到了很多求数量的题目,由于数量过于庞大,最终会要求返回
(int)(result % Long), 其中 Long代表一个比较大的数,比如 10^9 + 1
今天再次遇到了这种题目,是求:
(n! * m!) % q
其中 n m是 int , q是一个 long
勾起了我的兴趣,括号内 n! * m! 是极有可能超出Java中的Long取值范围的
题目
提示,这道题划分为Easy是有争议的,划分为Easy完全是因为时间限制的很宽裕而已...
笔者的解法如下
import java.util.*;
class Solution {
/* 100.00% 93.57% */
final long MOD = 1000000007;
public int numPrimeArrangements(int n) {
// 首先 0 不是质数
// 2 <= i <= n 质数的数量 n1
// 题目转变为
// 质数的可选位置总共有: n1 个数字的全排列
// n1! = n1 * (n1 - 1) * ... * 2 * 1
// 其他数字的可选位置有: (n - n1)!
// res = n1 * (n1 - 1)* * 2 * 1
// * (n - n1) * (n - n1 -1)* * 2 * 1
if (n == 1) return 1;
boolean[] isPrim = new boolean[n + 1];
Arrays.fill(isPrim, true);
int sqrt_n = (int)Math.sqrt(n);
for (int i = 2; i <= sqrt_n; i++) {
if (isPrim[i]) {
for (int j = i * i; j <= n; j += i) {
isPrim[j] = false;
}
}
}
int n1 = 0;
for (int i = 2; i <= n; i++) {
if (isPrim[i]) n1++;
}
return getAns(n, n1);
}
private int getAns(int n, int n1) {
// 插一条 % 运算的分配率
// (a * b) % p = (a % p * b % p) % p
int tmp = 0;
long res = 0; // res = n1! * (n - n1)!
long p = 1;
if (n - n1 > n1) {
// 先算 n1! 再算 (n - n1)!
tmp = n1;
while (tmp > 1) {
p *= tmp;
p %= MOD;
tmp--;
}
res = (p * p) % MOD;
for (int i = n1 + 1; i <= n - n1; i++) {
res *= i;
res %= MOD;
}
} else {
// 先算 (n - n1)! 再算 n1!
tmp = n - n1;
while (tmp > 1) {
p *= tmp;
p %= MOD;
tmp--;
}
res = (p * p) % MOD;
for (int i = n - n1 + 1; i <= n1 ; i++) {
res *= i;
res %= MOD;
}
}
return (int)(res % MOD);
}
}
思路就是
- 求出 [1,n] 中质数的个数 n1
- 分析可得,最终排列组合的个数 = 质数的排列 * 合数的排列
- 求质数的排列 n1!
- 求合数的排列 (n - n1)!
- 求 res = n1! * (n - n1)!
- 按要求返回 (int)( (n1! * (n - n1)! ) % 1000000007 )
- 中间用了一点节约运算步骤的技巧 :
- 比较 n1 和 n - n1的较小值 m
- 由于最终的结果 ,有重叠部分 1 * 2 * 3 * ... * m
- n1! * (n - n1)! = (m!) * (m!) * (m + 1) * (m + 2) * ... * Math.max(n1, n - n1)
中间发现 n1! * (n - n1)! 无法用Long接收,导致溢出,因此直觉告诉我取模运算可以简化
查阅互联网得到取模运算的分配率公式
其中重要的点在于乘法运算的取模分配率
(a * b) % p=(a%p * b%p) %p
因此 (n1! * (n - n1)! ) % 1000000007 可以写成
(( n1! % 1000000007 ) * ( (n - n1)! % 1000000007 ) ) % 1000000007
进一步,由于阶乘本身就是乘法运算,因此每一步计算阶乘时,都可以 % 1000000007
最终的结果再 % 1000000007 即可,参考上文中的取模代码
tmp = n1;
while (tmp > 1) {
p *= tmp;
p %= MOD;// 根据分配率先取模
tmp--;
}
res = (p * p) % MOD; // 根据分配率先取模
for (int i = n1 + 1; i <= n - n1; i++) {
res *= i;
res %= MOD; // 根据分配率先取模
}
return (int)(res % MOD);// 根据分配率括号外取模
标签:tmp,取模,运算,int,1000000007,应用,n1,MOD
From: https://www.cnblogs.com/jentreywang/p/16603038.html