素数
素数的定义:
素数也叫质数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做素数,否则就是合数(规定:1既不是质数也不是合数)。
寻找素数的方法:
试除法:
解释:试除法由名字可以知道就是根据定义用 2 到 N - 1 的数一个一个试除,如果 N不能整除之间所有的数,那么它就是素数。这样从 2 试到 N - 1,显然时间复杂度很高,我们有没有方法可以降低一些时间复杂度?
代码实现如下:
public class Util {
private Util() {
}
/**
*
* @param n 传进来的一个整数
* @return boolean
* @date 2023/8/5 13:24
* @description 试除法(判断素数)
*/
private static final int PRIME_START_NUM = 2;
public static boolean primeCommon(int n) {
if (n < PRIME_START_NUM) {
return false;
}
// 这里用 i <= n / i 或 i * i <= n 比 i <= sqrt(n)要好
for (int i = PRIME_START_NUM; i <= n / i; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
@Contract(pure = true)
public static int @NotNull [] primeRangeCommon(int n) {
int[] prime = new int[n];
for (int i = 2; i <= n; i++) {
if (primeCommon(i)) {
prime[i - PRIME_START_NUM] = i;
}
}
int count = 0;
for (int item : prime) {
if (item != 0) {
count++;
}
}
int[] outPutPrime = new int[count];
count = 0;
for (int value : prime) {
if (value != 0) {
outPutPrime[count++] = value;
}
}
return outPutPrime;
}
}
埃氏筛法:
解释:也是用于解决这类问题简单快速的方法,可以很快找到[2, n]中的所有素数,即从2开始寻找素数,每次找到一个素数后就将它的倍数全都过筛掉, 并将该素数存储到另一个数组中,连续循环,直至原始数组为null。
代码实现如下:
@Contract(pure = true)
private static int @NotNull [] prime(int n) {
// 声明一个计数器
int count = 0;
// 声明一个数组,用来存放素数
int[] prime = new int[n];
// 声明一个筛选数组 默认全false
boolean[] st = new boolean[n];
for (int i = PRIME_START_NUM; i <= n; i++) {
// 如果过筛,放进素数数组
if (!st[i - 1]) {
prime[count++] = i;
// 每找到一个素数后就将它的倍数全部筛选掉
// 这里可以优化成 int j = i * i
for (int j = PRIME_START_NUM * i; j <= n; j += i) {
st[j - 1] = true;
}
}
}
return prime;
}
@Contract(pure = true)
public static int @NotNull [] primeRange(int n) {
int count = 0;
for (int prime : prime(n)) {
if (prime != 0) {
count++;
}
}
int[] outPutPrime = new int[count];
if (outPutPrime.length >= 0) {
System.arraycopy(prime(n), 0, outPutPrime, 0, outPutPrime.length);
}
return outPutPrime;
}
(优化)欧拉筛法:
解释:埃氏筛法中,由于一个数有可能同时会是两个不同素数的倍数,即可能出现重复过筛(重复标记)的情况,即同一个数被过筛掉了不止一次,会浪费一部分资源。欧拉筛法就是在埃氏算法的逻辑之上多了一个判断的步骤,从而消去了这种重复过筛的情况,中心思路就是利用合数中的某一个因数过筛掉该合数本身,即利用已经求得的素数,第一环将区间内的数从小到大遍历,第二重环将已求知的素数进行升序遍历,将这个数和素数的乘积标记为合数。这时如果有一个数能被素数整除,立即跳出循环。
代码实现如下:
@Contract(pure = true)
private static int @NotNull [] primePlus(int n) {
// 声明一个计数器
int count = 0;
// 声明一个数组,用来存放素数
int[] prime = new int[n];
// 声明一个筛选数组 默认全false
boolean[] st = new boolean[n * n];
for (int i = PRIME_START_NUM; i <= n; i++) {
// 如果过筛,放进素数数组,
if (!st[i]) {
prime[count++] = i;
}
// 每找到一个素数后就将它的倍数全部筛选掉
for (int j = 0; prime[j] <= n / i; j++) {
st[prime[j] * i] = true;
if (i % prime[j] == 0) {
break;
}
}
}
return prime;
}
@Contract(pure = true)
public static int @NotNull [] primeRangePlus(int n) {
int count = 0;
for (int prime : primePlus(n)) {
if (prime != 0) {
count++;
}
}
int[] outPutPrime = new int[count];
if (outPutPrime.length >= 0) {
System.arraycopy(primePlus(n), 0, outPutPrime, 0, outPutPrime.length);
}
return outPutPrime;
}
@Contract(pure = true)
public static int @NotNull [] primeRangePlus(int n, int anotherN) {
// 起始[2,n]范围内的所有素数
int[] primeStart = primeRangePlus(n);
// 结束[2,anotherN]范围内的所有素数
int[] primeEnd = primeRangePlus(anotherN);
// 输出[n,anotherN]范围内的所有素数
int[] outPutPrime = new int[primeEnd.length - primeStart.length];
Arrays.fill(outPutPrime, primeEnd[primeStart.length]);
return outPutPrime;
}