判定素数的3种方法-枚举法、筛素数法、六素数法
素数的定义
一个大于 1 的自然数,除了 1 和 它自身外,不能被其他自然数整除的数叫做质数;否则称合数(规定 1 即不是质数也不是合数)
枚举法
思路
我们可以根据素数的定义一步步写出函数判断一个数是否是素数
1)一个大于 1 的自然数
static boolean isPrime(int num) {
// 根据素数定义可知素数为大于 1 的自然数
if (num < 2) return false;
//...
}
2)除了 1 和 它自身外,不能被其他自然数整除的数叫做质数
static boolean isPrime(int num) {
// 根据素数定义可知素数为大于 1 的自然数
if (num < 2) return false;
for (int i = 2; i < num; i++) {
// 若除了 1 和 它自身外,num 能被 i 整除,则为合数,返回 false
if (num % i == 0)
return false;
}
// 找不到除了 1 和 它自身外,能被其他自然数整除的数,返回 true
return true;
}
上面这个函数已经可以判断一个 int
类型的数是否为素数了。
根据数学知识我们可以判断出
i
i
i 的最大值取到 sqrt(num)
优化后
static boolean isPrime(int num) {
if (num < 2) return false;
int k = (int) Math.sqrt(num) + 1;
for (int i = 2; i < k; i++) {
if (num % i == 0)
return false;
}
return true;
}
优化后的函数将时间复杂度从 O ( n ) O(n) O(n) 降到 O ( n ) O(\sqrt{n}) O(n)
筛素数法
如果我们需要频繁的判断一些数是否为素数,使用筛素数法可以节约极大量的时间。
(定所求的最大值为 n n n, m m m 为小于等于 n n n 的素数个数,则枚举法所需的时间为 O ( n 2 ) O(n^2) O(n2),而筛素数法所需要的时间为 O ( m ∗ n ) O(m*n) O(m∗n))
思路
如果 i i i 为素数,则 2 ∗ i , 3 ∗ i , 4 ∗ i , . . . . 2 * i, 3 * i, 4* i,.... 2∗i,3∗i,4∗i,.... 这些数一定不为素数
根据这个思路,如过 i i i 是素数,我们则将 2 ∗ i , 3 ∗ i , 4 ∗ i , . . . . 2 * i, 3 * i, 4* i,.... 2∗i,3∗i,4∗i,.... 标记为合数
演示
第 1 轮筛选

第 2 轮筛选

第 3 轮筛选

第 k 轮筛选

代码
static boolean isPrime[MAX_N]
static void f() {
Arrays.fill(isPrime, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i < MAX_N; i++) {
if (isPrime[i]) {
for (int j = 2; j * i < MAX_N; j++) {
isPrime[j * i] = false;
}
}
}
}
六素数法
筛素数法已经非常高效了,可是若我们需要判断的 n u m num num 值远远大于 1 1 1,则我们浪费了许多不必要的内存开销,甚至 n u m num num 太大时,以至于无法开到这么大的数组。由此我们引入六素数法判断,此法极大的提高了优化后的枚举法。
思路
对于大于等于 5 5 5 的数,我们可以用 6 n , 6 n + 1 , 6 n + 2 , 6 n + 3 , 6 n + 4 , 6 n + 5 6n,6n+1,6n+2,6n+3,6n+4,6n+5 6n,6n+1,6n+2,6n+3,6n+4,6n+5 表示
又 6 6 6 的真因子有 1 、 2 、 3 1、2、3 1、2、3 ,则有
6 n 6n 6n 可以被 2 、 3 2、3 2、3 整除
6 n + 2 , 6 n + 4 6n+2, 6n+4 6n+2,6n+4 可以被 2 2 2 整除
6 n + 3 6n+3 6n+3 可以被 3 3 3 整除
则对于大于等于 5 5 5 的数,若其为素数则一定满足 6 n + 1 6n + 1 6n+1 或 6 n + 5 6n+5 6n+5 的形式
代码
static boolean isPrime(int num) {
if (num <= 3) return num > 1;
if (num % 6 != 1 && num % 6 != 5) return false;
int k = (int) Math.sqrt(num) + 1;
for (int i = 5; i < k; i += 6) {
if (num % i == 0 || num % (i + 2) == 0)
return false;
}
return true;
}
i i i 每次加 6,相比较普通的枚举法,6 素数法极大的提高了效率