@
目錄
暴力求質數
//最基礎的暴力求質數(我覺得這個的話,應該就不用多說了)
public static int getPrimes(int n) {
List<Integer> list = new ArrayList<>();
for (int i = 2; i < n; i++) {
boolean bool = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
bool = false;
break;
}
}
if (bool) {
list.add(i);
}
}
// System.out.println(list);
return list.size();
}
帶一些優化的暴力求質數
//帶一些優化的暴力求質數
public static int getPrimes1(int n) {
if (n < 3) {
return 0;
}
//從3開始驗算,所以初始值為1(2為質數)。
List<Integer> list = new ArrayList<>();
list.add(2);
for (int i = 3; i < n; i++) {
//當某個數為 2 的 n 次方時(n為自然數),其 & (n - 1) 所得值將等價於取余運算所得值
//*如果 x = 2^n ,則 x & (n - 1) == x % n
//if(i % 2 == 0)
if ((i & 1) == 0) {
continue;
}
boolean bool = true;
//用 j * j <= i 代替 j <= i 會更好。
//因為我們已經排除了所有偶數,所以每次循環加二將規避偶數會減少循環次數
for (int j = 3; j * j <= i; j += 2) {
if (i % j == 0) {
bool = false;
break;
}
}
if (bool) {
list.add(i);
}
}
// System.out.println(list);
return list.size();
}
通過前面求得的質數對后面的質數進行判斷
//通過前面求得的質數對后面的質數進行判斷
public static int getPrimes2(int n) {
//存放質數得list
List<Integer> list = new ArrayList<>();
//求質數從2開始
for (int m = 2; m < n; m++) {
//聲明一個bool變量,看看是不是在list里面有能被整除得
boolean isPrime = true;
//常見得求質數開平方
int sqrt = (int) Math.sqrt(m);
//循環list,list里面都是求過得質數
for (Integer i : list) {
//如果以前得質數能被n整除,說明n不是質數,false,list不能添加n
//直接終止循環
if (m % i == 0) {
isPrime = false;
break;
}
//如果i大於當前這個數得開平方數,證明后面的已經不可能整除了
if (i > sqrt) {
break;
}
}
//如果當前循環內,沒有被整除,即為質數,可以加入list,進行下一個循環
if (isPrime) {
list.add(m);
}
}
// System.out.println(list);
return list.size();
}
厄拉多塞篩法
//厄拉多塞篩法
public static int getPrimes3(int n) {
List<Integer> list = new ArrayList<>();
boolean[] bools = new boolean[n];
for (int i = 2; i < n; i++) {
// 布爾類型的默認值為 假。所以在此處用了邏輯非(!)操作符。
if (!bools[i]) {
list.add(i);
for (int j = i + i; j < n; j += i) {
//只要是i的倍數,就證明不是質數,因為他有其他因子
//排除不是質數的數
bools[j] = true;
}
}
}
// System.out.println(list);
return list.size();
}
Bitmap對篩法的空間優化(主要是空間優化,當然也有效率優化)
/*
Bitmap對篩法的空間優化(主要是空間優化,當然也有效率優化)
這個的意思就是signs是記錄的第幾層,每一層分為8個
也就是int為32字節,每個字節是8個bit
(i & 31)==(i % 32)
(1 << (i & 31))這一步是為了滿足位數
signs[i / 32]是為了看第幾層
((signs[i / 32] & (1 << (i & 31))) == 0證明當前這一層的這一位並沒有被記錄
說明當前是個質數
循環:
循環中的j就是i的倍數,既然是倍數,就說明他不是質數
signs[j / 32] |= 1 << (j & 31);
在看這里,找到j的那一層,然后看j那一層那幾位bit,如果有某一位有1存在,那這一位就是1
二進制下:
101 | 10 == 111
*/
public static int getPrimes4(int n) {
List<Integer> list = new ArrayList<>();
//一個 int 變量占用 32 字節
//如果是在C#中,提供了點陣列(BitArray)數組,那個的可讀性會好很多
int[] signs = new int[n / 32 + 1];
for (int i = 2; i < n; i++) {
//將元素和需確定得數字經行按位或運算,如果值改變,說明不存在該數字(未登記該數字),則其為質數。
//(當某個數為 2 的 n 次方時(n為自然數),其 & (n - 1) 所得值將等價於取余運算所得值)
//*如果 x = 2^n ,則 x & (n - 1) == x % n
//下面判斷可以寫成
//if ((signs[i / size] & (1 << (i % 32))) == 0)
if ((signs[i / 32] & (1 << (i & 31))) == 0) {
list.add(i);
for (int j = i + i; j < n; j += i) {
//登記該數字
signs[j / 32] |= 1 << (j & 31);
}
}
}
// System.out.println(list);
return list.size();
}
最后來看一下各個求質數方法的效果圖(這里用的是一百萬以內的質數)
這里附上全部代碼
import java.util.ArrayList;
import java.util.List;
public class Primes {
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(getPrimes(1000000));
long end = System.currentTimeMillis();
System.out.println("最基礎的暴力求質數" + (end - start) + "毫秒");
start = System.currentTimeMillis();
System.out.println(getPrimes1(1000000));
end = System.currentTimeMillis();
System.out.println("帶一些優化的暴力求質數" + (end - start) + "毫秒");
start = System.currentTimeMillis();
System.out.println(getPrimes2(1000000));
end = System.currentTimeMillis();
System.out.println("通過前面求得的質數對后面的質數進行判斷" + (end - start) + "毫秒");
start = System.currentTimeMillis();
System.out.println(getPrimes3(1000000));
end = System.currentTimeMillis();
System.out.println("厄拉多塞篩法" + (end - start) + "毫秒");
start = System.currentTimeMillis();
System.out.println(getPrimes4(1000000));
end = System.currentTimeMillis();
System.out.println("Bitmap對篩法的空間優化(主要是空間優化,當然也有效率優化)" + (end - start) + "毫秒");
}
//最基礎的暴力求質數
public static int getPrimes(int n) {
List<Integer> list = new ArrayList<>();
for (int i = 2; i < n; i++) {
boolean bool = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
bool = false;
break;
}
}
if (bool) {
list.add(i);
}
}
// System.out.println(list);
return list.size();
}
//帶一些優化的暴力求質數
public static int getPrimes1(int n) {
if (n < 3) {
return 0;
}
//從3開始驗算,所以初始值為1(2為質數)。
List<Integer> list = new ArrayList<>();
list.add(2);
for (int i = 3; i < n; i++) {
//當某個數為 2 的 n 次方時(n為自然數),其 & (n - 1) 所得值將等價於取余運算所得值
//*如果 x = 2^n ,則 x & (n - 1) == x % n
//if(i % 2 == 0)
if ((i & 1) == 0) {
continue;
}
boolean bool = true;
//用 j * j <= i 代替 j <= i 會更好。
//因為我們已經排除了所有偶數,所以每次循環加二將規避偶數會減少循環次數
for (int j = 3; j * j <= i; j += 2) {
if (i % j == 0) {
bool = false;
break;
}
}
if (bool) {
list.add(i);
}
}
// System.out.println(list);
return list.size();
}
//通過前面求得的質數對后面的質數進行判斷
public static int getPrimes2(int n) {
//存放質數得list
List<Integer> list = new ArrayList<>();
//求質數從2開始
for (int m = 2; m < n; m++) {
//聲明一個bool變量,看看是不是在list里面有能被整除得
boolean isPrime = true;
//常見得求質數開平方
int sqrt = (int) Math.sqrt(m);
//循環list,list里面都是求過得質數
for (Integer i : list) {
//如果以前得質數能被n整除,說明n不是質數,false,list不能添加n
//直接終止循環
if (m % i == 0) {
isPrime = false;
break;
}
//如果i大於當前這個數得開平方數,證明后面的已經不可能整除了
if (i > sqrt) {
break;
}
}
//如果當前循環內,沒有被整除,即為質數,可以加入list,進行下一個循環
if (isPrime) {
list.add(m);
}
}
// System.out.println(list);
return list.size();
}
//厄拉多塞篩法
public static int getPrimes3(int n) {
List<Integer> list = new ArrayList<>();
boolean[] bools = new boolean[n];
for (int i = 2; i < n; i++) {
// 布爾類型的默認值為 假。所以在此處用了邏輯非(!)操作符。
if (!bools[i]) {
list.add(i);
for (int j = i + i; j < n; j += i) {
//只要是i的倍數,就證明不是質數,因為他有其他因子
//排除不是質數的數
bools[j] = true;
}
}
}
// System.out.println(list);
return list.size();
}
/*
Bitmap對篩法的空間優化(主要是空間優化,當然也有效率優化)
這個的意思就是signs是記錄的第幾層,每一層分為8個
也就是int為32字節,每個字節是8個bit
(i & 31)==(i % 32)
(1 << (i & 31))這一步是為了滿足位數
signs[i / 32]是為了看第幾層
((signs[i / 32] & (1 << (i & 31))) == 0證明當前這一層的這一位並沒有被記錄
說明當前是個質數
循環:
循環中的j就是i的倍數,既然是倍數,就說明他不是質數
signs[j / 32] |= 1 << (j & 31);
在看這里,找到j的那一層,然后看j那一層那幾位bit,如果有某一位有1存在,那這一位就是1
二進制下:
101 | 10 == 111
*/
public static int getPrimes4(int n) {
List<Integer> list = new ArrayList<>();
//一個 int 變量占用 32 字節
//如果是在C#中,提供了點陣列(BitArray)數組,那個的可讀性會好很多
int[] signs = new int[n / 32 + 1];
for (int i = 2; i < n; i++) {
//將元素和需確定得數字經行按位或運算,如果值改變,說明不存在該數字(未登記該數字),則其為質數。
//(當某個數為 2 的 n 次方時(n為自然數),其 & (n - 1) 所得值將等價於取余運算所得值)
//*如果 x = 2^n ,則 x & (n - 1) == x % n
//下面判斷可以寫成
//if ((signs[i / size] & (1 << (i % 32))) == 0)
if ((signs[i / 32] & (1 << (i & 31))) == 0) {
list.add(i);
for (int j = i + i; j < n; j += i) {
//登記該數字
signs[j / 32] |= 1 << (j & 31);
}
}
}
// System.out.println(list);
return list.size();
}
}