• 判断一个数是否是质数


    判断一个数是否是质数

    方法1.

    在大于 1 的自然数中,如果 num 有除了 1 和自身以外的因数,说明 num 不是质数,返回 0。

    最简单的方法是 i 从 2 到 num-1 都试一遍,看是否能整除 num。

    • 若 i 能整除 num,则 num 不是质数,返回 0.
    • 若循环结束,没找到能整除 num 的 i,则 num 是质数,返回 1.

    设 num 为合数,有 num = i * j。若 i > √num,推出 j 的取值范围。

    i = num/j > √num
    num > √num * j
    等式两边同时除以 √num:√num > j
    整理得:j < √num
    
    • 1
    • 2
    • 3
    • 4

    一个因数 i > √num,则另一个因数 j < √num。在 [2, √num] 范围内,必有一整数能整除 num。换言之,如果遍历到 i > √num 时,未能有 i 能整除 num,则 num 为质数。

    所以 i 没必要从 2 到 num-1,从 2 到 √num 就行。

    例 53,√53 ≈ 7.28,当循环到 i = 8 时,假设 53 为合数,53 = 8 * j,8 > √53,所以 j < √53,j <= 7,而在之前的遍历中 i = 2、i = 3、… i = 7 并没有出现 num % i == 0 的情况,否则循环会直接终止,所以没有这样的 j 能整除 num。

    i <= √num
    等式两边平方得:i * i <= num
    考虑到 i * i 容易溢出,转为:i <= num/i
    
    • 1
    • 2
    • 3
    int isPrime(int num) {
        if (num <= 1) {
    		return 0;
    	}
        for (int i = 2; i <= num / i; i++) {
            // 若 i 能整除 num,则 i 是 num 的因数,所以 num 不是质数,循环终止
            if (num % i == 0) {
                return 0;
            }
        }
        return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方法1、2、3 计算超过 10 亿量级的质数个数有些困难,前者是因为效率低,后者是空间不足。

    方法2.

    创建数组 primeArr,下标为 num。

    • primeArr[num] 为 0,代表 num 是质数。
    • primeArr[num] 为 1,代表 num 是合数。
    • primeArr[num] 为 -1,代表 num 既不是合数也不是质数。

    所有的合数可以分解为多个质数的乘积,设 num 为质数,所有的合数便可以使用 num * k 表示,primeArr[num * k] = 1,k 从 2 到 (length-1)/num。

    但请看:

    2:4, 6, 8, 10...
    3:6, 9, 12, 15...
    5:10, 15, 20, 25...
    
    • 1
    • 2
    • 3

    第一轮循环 primeArr[2] = 0、primeArr[4] = 1、primeArr[6] = 1…

    第二轮循环 primeArr[3] = 0、primeArr[6] = 1、primeArr[9] = 1…

    primeArr[6] 重复设置。

    primeArr[3 * 2] 已在 primeArr[2 * 3] 时被设置为 1。

    同样的 primeArr[4 * 2] 已在 primeArr[2 * 4] 时被设置为 1;primeArr[4 * 3] 已在 primeArr[3 * 4] 时设置为 1…

    综上,k ∈ [2, num-1] 已经在之前的循环中被设置,不妨令 k 从 num 开始。

    2:4, 6, 8, 10...
    3:9, 12, 15, 18...
    5:25, 30, 35, 40...
    
    • 1
    • 2
    • 3
    #include 
    
    #define MAX_LENG 100000
    static int primeArr[MAX_LENG];
    static int isInit = 0;
    
    int primeInit() {
        int primeCount = 0;
        primeArr[0] = primeArr[1] = -1;
        
        for (int num = 2; num < MAX_LENG; num++) {
            if (primeArr[num] == 0) {
                primeCount++;
                // 怕 k * num 溢出
                for (int k = num; k < (MAX_LENG) / (num+0.0); k++) {
                    primeArr[num * k] = 1;
                }
                // 或者使用 i += num,i 为 num 的倍数
                /*
                	for (int i = num * num; i < MAX_LENG; i += num) {
                		primeArr[i] = 1;
                	}
                */
            }
        }
        isInit = 1;
        return primeCount;
    }
    int isPrime(int num) {
        if (num >= MAX_LENG) {
            printf("只支持 [0, %d] 范围内的查询\n", MAX_LENG-1);
            return 0;
        }
        if (!isInit) {
            int primeCount = primeInit();
            printf("[0, %d] 范围内的质数个数:%d\n", MAX_LENG-1, primeCount);
        }
        return primeArr[num] == 0 ? 1 : 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    方法3.

    方法 2 中也有很多数被重复设置,如

    • 12:2 * 6、3 * 4 重复设置。
    • 18:2 * 9、3 * 6 重复设置。
    • 24:2 * 12、3 * 8 重复设置。
    • 45:5 * 9、3 * 15 重复设置。

    所以考虑换个方案。

    让 k 从 2 到 num。k >= num+1 的部分交给下几轮循环,如 num = 4,k = 5 可以交给 num = 5,k = 4。

    2:4
    3:6,9
    4:8,12,16
    5:10,15,20,25
    6:12,18,24,30,26
    7:14,21,28,35,42,49
    8:16,24,32,40,48,56,64
    9:18,27,36,45,54,63,72,81
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意看,第 3 行、第 5 行的 12 重复。

    num % k == 0
    设 num / k = n
    那么 num * (k+1) = n*k * (k+1)
    num * (k+1) 在之后的循环 num = n*(k+1) 时重复设置
    
    类似的 num * (k+2) = n*(k+2) * k 重复设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这意味当 k 能整除 num 时,k 没必要++,num * (k+?) 会在之后的循环中被设置,本次循环应到此终止。

    例 num = 9,k = 3 时,9 % 3 == 0,9 * 4 = 3 * 3 * 4 = 12 * 3,36 被 9 * 4 和 12 * 3 重复设置。

    例 当 num 为偶数,k = 2 时,num % k == 0,则 k 到此终止,没必要++。如 num = 4,k = 2,k = 3、k = 4… 留给 num = 6、num = 8… 设置。

    2:4
    3:6,9
    4:8
    5:10,15,20,25
    6:12
    7:14,21,28,35,42,49
    8:16
    9:18,27
    10:20
    11:22,33,44,55,66,77,88,99,110,121
    12:24
    13:26,39,52,65,78,91,104,117,130,143,156,169
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意,第 10 行的 44 = 11 * 4 = 11 * 2 * 2 = 22 * 2。

    由于合数 4 可以分解为多个质数的乘积,所以在 num = 22,k = 2 时,也重复设置了。需要规定 k 必须为质数,即 k = 2、3、5、7、11、13、17…

    24
    369
    48
    5101525
    612
    714213549
    816
    91827
    1020
    1122335577121
    1224
    1326396591143169
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    #include 
    
    #define MAX_LENG 1000000
    static int primeArr[MAX_LENG];
    static int isInit = 0;
    
    int primeInit() {
        primeArr[0] = primeArr[1] = -1;
    	int primeCount = 0;
    
        for (int num = 2; num < MAX_LENG; num++) {
    		if (primeArr[num] == 0) {
    			primeCount++;
    		}
    		// k ∈ [2, num]
            // 第2个条件可以修改为 k < (MAX_LENG) / (num+0.0)
            for (int k = 2; k <= num && k*num < MAX_LENG; k++) {
    			// k 必须为质数
                if (primeArr[k] == 0) {
                	primeArr[num * k] = 1;
    				// 若 num % k == 0,设 num = n * k
    				// num *(k+1)、num *(k+2)... 留给 num = n*(k+1)、n*(k+2)...时设置
                	if (num % k == 0) break;
                }
            }
        }
        isInit = 1;
    	return primeCount;
    }
    int isPrime(int num) {
        if (num >= MAX_LENG) {
            printf("只支持 [0, %d] 范围内的查询\n", MAX_LENG-1);
            return 0;
        }
        if (!isInit) {
            int primeCount = primeInit();
    		printf("[0, %d] 范围内的质数个数:%d\n", MAX_LENG-1, primeCount);
        }
        return primeArr[num] == 0 ? 1 : 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    10以内的质数个数:4
    100以内的质数个数:25
    1000以内的质数个数:168
    10000以内的质数个数:1229
    100000以内的质数个数:9592
    1000000以内的质数个数:78498
    10000000以内的质数个数:664579
    100000000以内的质数个数:5761455
    1000000000以内的质数个数:50847534
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    DQL、DML、DDL、DCL的概念与区别
    C++:构造函数与析构函数
    征服地球极限,中国极地科考与登峰事业的“御寒”之旅
    技术干货 | 基于 MindSpore 实现图像分割之豪斯多夫距离
    安卓原生项目工程结构说明
    被裁后,狂刷607页JUC源码分析笔记,立马拿蚂蚁offer
    ZCU102 Zynq MPSoC IP设置与说明
    字符串解码
    CPU核检测
    增长黑客营销方式
  • 原文地址:https://blog.csdn.net/cqh123hh/article/details/126639991