LeetCode No.1175
这里用的埃氏筛。因为做题很少刷到筛法相关,会忘,所以还是整理。
class Solution {
public int numPrimeArrangements(int n) {
final int mod = 1000000007;
if (n == 1) {
return 1;
}
// 转为求 1 到 n 有几个质数
// 使用埃式筛法
int count = 0;
boolean[] isNotPrime = new boolean[n + 1];
for (int i = 2; i <= n; i++) {
if (!isNotPrime[i]) {
count++;
for (int j = 1; j*i <= n; j++) {
isNotPrime[j*i] = true;
}
}
}
long result = 1;
for (int i = 2; i <= count; i++) {
result = Math.floorMod(result * i, mod);
}
for (int i = 2; i <= n-count; i++) {
result = Math.floorMod(result * i, mod);
}
return (int)result;
}
}
埃氏筛
因为质数的倍数不为质数,且已知最小的质数为 2。则从 2 开始,对质数进行遍历,将当前质数的 n 倍数都进行标记,不带标记的则为质数,可继续参与遍历。
欧拉筛
埃氏筛存在重复标记的问题,如对质数 2 和 3 的倍数进行标记时,{6, 12, 18, 24 …} 都会被重复标记。
当给定的范围越大,则重复标记的情况越严重。
欧拉筛通过最小质数因子判断来结束逻辑。
1175 使用欧拉筛处理。
class Solution {
public int numPrimeArrangements(int n) {
final int mod = 1000000007;
if (n == 1) {
return 1;
}
// 转为求 1 到 n 有几个质数
// 使用欧拉筛法
int count = 0;
boolean[] isNotPrime = new boolean[n + 1];
int[] primeArray = new int[n];
// 从最小质数 2 开始遍历
for (int i = 2; i <= n; i++) {
// 如果是质数,则加入质数队列
if (!isNotPrime[i]) {
primeArray[count++] = i;
}
// 当前数 i 和 质数队列里的质数依次相乘,产生的合数都不为质数
for (int j = 0; j < n && i*primeArray[j] <= n; j++) {
isNotPrime[i*primeArray[j]] = true;
// 关键步骤,i 在质数队列中找到最小质数因子,停止
if (i % primeArray[j] == 0) {
break;
}
}
}
long result = 1;
for (int i = 2; i <= count; i++) {
result = Math.floorMod(result * i, mod);
}
for (int i = 2; i <= n-count; i++) {
result = Math.floorMod(result * i, mod);
}
return (int)result;
}
}