素数序列的生成及其应用(采用了自研的高效算法)
程序员文章站
2022-04-24 20:26:22
问题: 2000以内的素数有哪些? 她的手机号是素数吗? 思路: 问题归类: 怎样获取n以内的所有素数呢? 怎样高效地判定一个正整数是否为素数呢? 假设已知: 第一个素数是2,第二个素数是3; 断定某正整数n确实为素数的依据是:当且仅当 若不超过n的算数平方根的所有素数都不能整除n,那么就断定n为素 ......
问题:
- 2000以内的素数有哪些?
- 她的手机号是素数吗?
思路:
问题归类:
- 怎样获取n以内的所有素数呢?
- 怎样高效地判定一个正整数是否为素数呢?
假设已知:
第一个素数是2,第二个素数是3;
断定某正整数n确实为素数的依据是:当且仅当 若不超过n的算数平方根的所有素数都不能整除n,那么就断定n为素数。
酝酿策略:
先解决“获取n以内的所有素数”的问题,然后应用其结果来解决“高效地判定一个正整数是否为素数”的问题。
从第三个待定素数开始,以后总是把下一个候选素数的初始值取为当前已知的最后一个素数(必为奇数);
然后执行下述循环
LOOP
EXIT WHEN 候选素数超过n;
把该待定素数(为奇数)的值更新为其下一个奇数的值:prime_number_candidate += 2;
探测该待定素数是否确实为素数:若是,则把它作为正式的素数写到素数数组中下一个素数的位置上;否则,直接去继续下一轮循环。
END LOOP
代码实践:
头文件:prime_number.h
1 #ifndef PRIME_NUMBER_H 2 #define PRIME_NUMBER_H 3 4 /*制定 素数数组的元素以及计数下标的数据类型*/ 5 typedef unsigned long long PRIME_INTEGER_TYPE, PRIME_INTEGER_ARRAY_COUNT_TYPE; 6 7 /*制定 容量可动态调整的素数数组容器数据类型*/ 8 typedef struct { 9 PRIME_INTEGER_TYPE *prime_integer_array; // 容量可动态调整的素数数组 10 PRIME_INTEGER_ARRAY_COUNT_TYPE count; // 素数数组中元素的数目 11 }PRIME_INTEGER_ARRAY_TYPE; 12 13 /************************************全局常量的声明************************************/ 14 extern const PRIME_INTEGER_ARRAY_TYPE EMPTY_PRIME_INTEGER_ARRAY; 15 extern const char *PRIME_INTEGER_TYPE_PRINT_FORMAT_STRING, *PRIME_INTEGER_ARRAY_COUNT_TYPE_PRINT_FORMAT_STRING; 16 17 /**************************************方法的声明**************************************/ 18 19 /*获取upper_limit以内的素数数组*/ 20 PRIME_INTEGER_ARRAY_TYPE getPrimeIntegerArrayBelow(PRIME_INTEGER_TYPE upper_limit); 21 22 /*格式化地打印upper_limit以内的全部素数*/ 23 void printPrimeIntegerArrayBelow(PRIME_INTEGER_TYPE upper_limit); 24 25 /*判断一个给定的数是否为素数*/ 26 int isPrimeInteger(PRIME_INTEGER_TYPE n); 27 28 #endif
源文件:prime_number.c
1 #include "prime_number.h" 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <math.h> 5 6 /************************************全局常量的定义************************************/ 7 const PRIME_INTEGER_ARRAY_TYPE EMPTY_PRIME_INTEGER_ARRAY = {NULL, 0}; // 空素数数组 8 const char *PRIME_INTEGER_TYPE_PRINT_FORMAT_STRING = "%2llu", *PRIME_INTEGER_ARRAY_COUNT_TYPE_PRINT_FORMAT_STRING = "%02llu"; // 设置打印素数数组时元素及下标的输出格式符 9 10 /**************************************方法的定义**************************************/ 11 12 /*获取素数探测者的上限*/ 13 PRIME_INTEGER_TYPE getProberUpperLimitFor(PRIME_INTEGER_TYPE prime_integer_candidate) { 14 /*取素数候选者的算术平方根的去尾近似值作为用来探测它是否确实为素数的素数探测者的上限*/
15 return llroundl(sqrtl(prime_integer_candidate*1.0));
16 } 17 18 /*获取upper_limit以内的素数数组*/ 19 PRIME_INTEGER_ARRAY_TYPE getPrimeIntegerArrayBelow(PRIME_INTEGER_TYPE upper_limit) { 20 /*最小的素数为2,因此在2之前的素数数组为空素数数组*/ 21 if (upper_limit < 2) { 22 return EMPTY_PRIME_INTEGER_ARRAY; 23 } 24 /*在upper_limit以内顶多有max_count个素数,*/ 25 PRIME_INTEGER_ARRAY_COUNT_TYPE max_count = (upper_limit + 1) / 2; 26 /*开辟足够的空间用来容纳upper_limit以内的全部素数*/ 27 PRIME_INTEGER_TYPE *prime_integer_array = (PRIME_INTEGER_TYPE *)malloc((size_t)(max_count) * sizeof(PRIME_INTEGER_TYPE)); 28 if (prime_integer_array == NULL) { 29 printf("Failed when doing malloc.\n"); 30 exit(1); 31 } 32 /*装载第一个素数*/ 33 prime_integer_array[0] = 2; 34 if (upper_limit == 2) { 35 return (PRIME_INTEGER_ARRAY_TYPE) { prime_integer_array, 1 }; 36 } 37 /*装载第二个素数*/ 38 prime_integer_array[1] = 3; 39 if (upper_limit <= 4) { 40 return (PRIME_INTEGER_ARRAY_TYPE) { prime_integer_array, 2 }; 41 } 42 /*后续素数均通过算法来求取*/ 43 PRIME_INTEGER_ARRAY_COUNT_TYPE i = 1; // 接下来会用作while循环的步进变量 44 PRIME_INTEGER_TYPE next_prime_integer_candidate = prime_integer_array[i], prober_upper_limit; 45 int flag = 1; // 接下来会把flag当作布尔变量来用 46 PRIME_INTEGER_ARRAY_COUNT_TYPE j; // 接下来会用作内部for循环的步进变量 47 /*仅装载upper_limit以内的全部素数,若已装载完毕就退出循环*/ 48 while (/*i < max_count && */(next_prime_integer_candidate += 2) <= upper_limit) { 49 /*取下一个素数候选者的算术平方根的去尾近似值作为用来探测下一个素数候选者是否确实为素数的探测者的上限*/ 50 prober_upper_limit = getProberUpperLimitFor(next_prime_integer_candidate); 51 /*从第二个素数开始,依次用prober_upper_limit以内的素数来探测下一个素数候选者是否确实为素数*/ 52 for (j = 1; 53 /*j <= i &&*/ 54 /*只需用prober_upper_limit以内的素数来探测*/ 55 (flag = prime_integer_array[j] <= prober_upper_limit) 56 && 57 /*当且仅当prober_upper_limit以内的所有素数都不能整除下一个素数候选者时,才断定下一个素数候选者确实为素数*/ 58 (next_prime_integer_candidate % prime_integer_array[j] != 0); 59 ++j) 60 ; 61 /* 退出上面的for循环的原因有两种: 62 ** (1)prober_upper_limit以内的素数都已经作为探测者使用过了也没能否认下一个素数候选者 63 ** (2)下一个素数候选者被prober_upper_limit以内的某个素数整除了*/ 64 /*若是因为原因(1)而退出上面的for循环,那么就可断定下一个素数候选者确实为素数*/ 65 if (flag == 0) { 66 /*把下一个素数候选者正式作为下一个素数写入素数数组的下一个位置中*/ 67 prime_integer_array[++i] = next_prime_integer_candidate; 68 /*最终i的值也就是prime_integer_array的最大有效下标*/ 69 } 70 } 71 /*已知确切的素数个数,那么就可以把prime_integer_array所占的空间收缩到恰到好处的大小吧*/ 72 prime_integer_array = (PRIME_INTEGER_TYPE *)realloc(prime_integer_array, (size_t)(i + 1)* sizeof(PRIME_INTEGER_TYPE)); 73 return (PRIME_INTEGER_ARRAY_TYPE) { prime_integer_array, i + 1 }; 74 } 75 76 /*格式化地打印upper_limit以内的全部素数*/ 77 void printPrimeIntegerArrayBelow(PRIME_INTEGER_TYPE upper_limit) { 78 PRIME_INTEGER_ARRAY_TYPE pia = getPrimeIntegerArrayBelow(upper_limit); 79 if (pia.count == 0) { 80 printf("EMPTY_PRIME_INTEGER_ARRAY\n"); 81 } 82 else { 83 PRIME_INTEGER_ARRAY_COUNT_TYPE i = 0; 84 while (i < pia.count) { 85 printf("PrimeIntegerArray("); 86 printf(PRIME_INTEGER_ARRAY_COUNT_TYPE_PRINT_FORMAT_STRING, i); 87 printf(") = "); 88 printf(PRIME_INTEGER_TYPE_PRINT_FORMAT_STRING, pia.prime_integer_array[i]); 89 printf("\n"); 90 ++i; 91 } 92 } 93 } 94 95 /* 96 **判断一个给定的数是否为素数 97 **思路:当且仅当 若不超过其算数平方根的所有素数都不能整除n,那么断定n为素数; 98 */ 99 int isPrimeInteger(PRIME_INTEGER_TYPE n) { 100 if (n < 2) { 101 return 0; 102 } 103 if (n == 2 || n == 3 || n == 5 || n == 7) { 104 return 1; 105 } 106 if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0) { 107 return 0; 108 } 109 /*取下一个素数候选者的算术平方根的去尾近似值作为用来探测下一个素数候选者是否确实为素数的探测者的上限*/ 110 PRIME_INTEGER_TYPE prober_upper_limit = getProberUpperLimitFor(n); 111 /*生成探测者的上限以内的素数数组*/ 112 PRIME_INTEGER_ARRAY_TYPE pia = getPrimeIntegerArrayBelow(prober_upper_limit); 113 /*在上面的if语句中,2, 3, 5, 7这前4个素数已经用过了,下面直接从素数数组中第五个(其下标为4)的那个素数开始作为探测者*/ 114 PRIME_INTEGER_ARRAY_COUNT_TYPE i = 4; 115 while (i < pia.count) { 116 /*一旦被素数数组中的某素数否认,则直接返回“假”(这里用0表示“假”,用1表示“真”)*/ 117 if (n % pia.prime_integer_array[i] == 0) { 118 return 0; 119 } 120 ++i; 121 } 122 /*程序能执行到这一行,就能肯定n确实为素数。因为如果n不是素数,那么在上面的while循环中就已经直接return退出了*/ 123 return 1; 124 }
源文件:main.c
1 #include "prime_number.h" 2 #include <stdio.h> 3 4 int main(int argc, char **argv) { 5 printPrimeIntegerArrayBelow(100); 6
7 printf("isPrimeInteger(65537) : %s\n", isPrimeInteger(65537) == 1?"Yes":"No"); 8 return 0; 9 }
运行展示:
Microsoft Visual Studio Enterprise 2017 version 15.6.6 on Windows 10 Pro 1709
GCC version 7.3.1 on Manjaro Linux
下一篇: 一步之遥——蓝桥杯