本文总结归纳于翁恺老师MOOC的相关内容。
关于素数可能有许多不同的题目要求,例如输入数据单纯的进行素数判断,又或者进行给定范围内的素数加和,又或者要求制作素数表,等等。因此可以考虑将素数判断单独写为函数,在实现不同要求的时候直接进行调用。本文首先介绍素数判断的基础方法及其简化方法,随后针对构造素数表的要求给出了具体代码。
一、素数判断函数
1.最基础的:利用2到x-1是否能被输入整除进行判断
/**
利用2到x-1是否能被输入整除进行判断
@param x 待判断是否为素数的正数
*/
int isPrime(int x)
{
int i;
int ret = 1; //素数:1 非素数:0
if( x==1 ) ret = 0; // 1不是素数
for( i = 2 ; i<x ; i++ ){
if( x%i==0 ){
ret = 0;
break;
}
}
return ret;
}
2.优化(减少循环次数):除2外的偶数均非素数,故可去除偶数后从3开始遍历至x-1,每次加2,无整除则为素数
/**
除2外的偶数均非素数,故去除偶数后,从3到x-1,每次+2 ,遍历是否能被输入整除进行判断
@param x 待判断是否为素数的正数
*/
int isPrime(int x)
{
int i;
int ret = 1; //素数:1 非素数:0
if( x==1 || ( x%2==0 && x!=2 )) ret = 0; // 1不是素数
for( i = 3 ; i<x ; i+=2 ){
if( x%i==0 ){
ret = 0;
break;
}
}
return ret;
}
3.进一步优化:非素数一定能整除不超过其平方根的某个正数
/**
非素数一定能够整除一个不超过其平方根的整数(因式分解)
@param x 待判断是否为素数的正数
*/
int isPrime(int x)
{
int i;
int ret = 1; //素数:1 非素数:0
if( x==1 || ( x%2==0 && x!=2 )) ret = 0; // 1不是素数
for( i = 3 ; i<=sqrt(x) ; i+=2 ){
if( x%i==0 ){
ret = 0;
break;
}
}
return ret;
}
4. 另一种方案:判断能否被已知的且小于x的素数所整除,若不能,则x为素数
该方法也需要进行遍历,但遍历的是小于x的素数,故需要知晓小于输入的素数表,考虑到最终要求是构造素数表,这种函数也是方便的
/**
判断能否被已知的且小于x的素数所整除,若不能,则x为素数
@param x 待判断是否为素数的正数
@param KnownPrimes 小于x的所有素数构成的数组
@param number0fKnownPrimes 数组KnownPrimes的大小
*/
int isPrime(int x, int KnownPrimes[], int number0fKnownPrimes)
{
int i;
int ret = 1; //素数:1 非素数:0
for( i=0 ; i<number0fKnownPrimes; i++){
if( x%KnownPrimes[i]==0 ){
ret = 0;
break;
}
}
return ret;
}
二、构造素数表
1.方案一:对于每个数判断是否为素数,如果是素数则计入数组
由于前三种素数判断函数的参数和第四种素数判断函数的参数不同,故在声明时是不同的,在调用函数时也需要注意
需要注意:不能用变量声明数组大小的同时初始化,即int prime[number] ={2}会报错
#include<stdio.h>
#include<math.h> // sqrt()
//int isPrime(int x); // 对应前三种素数判断函数
int isPrime(int x, int KnownPrimes[], int number0fKnownPrimes); // 被已知的且小于x的素数所整除法
int main()
{
const int number = 10; // 素数表最大包括10个素数
int prime[10] = {2}; // 初始化,直接写入2,剩余数组元素初始化为0,另外,如果数组大小定义为number会报错,原因是:不能用变量声明数组大小的同时初始化
int count = 1; // 计数素数表中素数的个数
int i = 3; //从3开始判断是否为素数
while( count<number ){
if( isPrime(i, prime, count) ){ // 被已知的且小于x的素数所整除法
// if( isPrime(i) ){ //是素数时返回值为1 ,对应前三种素数判断函数
prime[count++] = i; //如果i是素数,将其写入数组下标count位置,并使count=count+1,指向下一个素数应该写入的数组位置
}
i ++;
}
// 输出质数表
for( i=0 ; i<number ; i++ ){
printf("%d", prime[i]);
if( (i+1)%5 ) printf("\t"); //5个数字一行制表
else printf("\n");
}
return 0;
}
对于程序往往需要进行调试,可以用大括号内部放置调试语句,内部的本地变量对外部语句不会造成影响,方便调试,例如我们可以在上面的代码中加一些语句查看具体的运行过程
// 加入调试语句(过程展示)
#include<stdio.h>
#include<math.h> // sqrt()
//int isPrime(int x); // 遍历
int isPrime(int x, int KnownPrimes[], int number0fKnownPrimes); // 被已知的且小于x的素数所整除法
int main()
{
const int number = 10; // 素数表最大包括10个素数
int prime[10] = {2}; // 初始化,直接写入2,剩余数组元素初始化为0
int count = 1; // 计数素数表中素数的个数
int i = 3; //从3开始判断是否为素数
{ // 调试用(加表头)
int i;
printf("\t\t\t");
for( i=0 ; i<number ; i++ ){
printf("%d\t", i); // 展示数组下标
}
printf("\n");
}
while( count<number ){
if( isPrime(i, prime, count) ){ // 被已知的且小于x的素数所整除法
// if( isPrime(i) ){ //是素数时返回值为1 ,遍历
prime[count++] = i; //如果i是素数,将其写入数组下标count位置,并使count=count+1,指向下一个素数应该写入的数组位置
}
{ // 调试用
printf("i=%d\t count=%d\t", i, count);
int i;
for( i=0 ; i<number ; i++){
printf("%d\t", prime[i]);
}
printf("\n");
}
i ++;
}
// 输出质数表
for( i=0 ; i<number ; i++ ){
printf("%d", prime[i]);
if( (i+1)%5 ) printf("\t"); //5个数字一行制表
else printf("\n");
}
return 0;
}
2.方案二:构造n以内(不含n)的素数表,可:①令x=2; ②将2x,3x,4x,...所有满足ax<n的整数标记为非素数;③令x到下一个没有被标记为非素数的数,重复步骤②,直至n以内所有数都尝试完毕
伪代码为:
// 构造质数表
#include<stdio.h>
int main()
{
int n;
printf("您想构造的素数表范围为小于:");
scanf("%d", &n);
int isPrime[n]; // 储存对应下标的数字是否为素数的指示值,例如isPrime[2]=1则表示2是素数
int i,j;
// 数组初始化
for( i=0 ; i<n ; i++){
isPrime[i] = 1;
}
// i遍历2至n-1的数,如果i是素数,则j从2倍开始,满足j*i<n的整数j*i标记为非素数
for( i=2 ; i<n ; i++){ //从2开始对于质数才有意义
if( isPrime[i]==1 ){
for( j=2 ; j*i<n ; j++){ //j表示倍数
isPrime[j*i] = 0;
}
}
}
// 质数表输出
for( i=2 ; i<n ; i++){
if( isPrime[i] ){
printf("%d\t", i);
}
}
printf("\n");
return 0;
}