求質數的幾種方法


@

暴力求質數


//最基礎的暴力求質數(我覺得這個的話,應該就不用多說了)
    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();
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM