C语言练习之求质数
前言
质数又称素数,指整数在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。换句话说,只有两个正因数(1和自己)的自然数即为质数。比1大但不是质数的数称为合数。0和1既非质数也非合数。
下面介绍基础的与质数相关的C语言练习。
输出质数
题目要求:输出100以内的所有质数
这题最常见的方式是用两层for循环实现。第一层将digit遍历从3到100的所有奇数(2可以直接输出),第二层循环定义一个除数divisor。其中内循环通常有三种方式:divisor < digit; divisor < (digit / 2); 引入math.h库divisor < sqrt(digit)。
第一种
#include <stdio.h>
int main() {
printf("2\n");
int digit;
int divisor;
for (digit = 3; digit <= 100; digit += 2)
{
for (divisor = 2; divisor < digit; divisor ++)
{
if (digit % divisor == 0)
break;
}
if(divisor == digit)
printf("%d\n",digit);
}
return 0;
}
第二种
#include <stdio.h>
int main() {
printf("2\n");
int digit;
int divisor;
for (digit = 3; digit <= 100; digit += 2)
{
for (divisor = 2; divisor < (digit / 2); divisor ++)
{
if (digit % divisor == 0)
break;
}
if(divisor >= digit/2)
printf("%d\n",digit);
}
return 0;
}
第三种
#include <stdio.h>
#include <math.h>
int main() {
printf("2\n");
int digit;
int divisor;
for (digit = 3; digit <= 100; digit += 2)
{
for (divisor = 2; divisor < sqrt(digit); divisor ++)
{
if (digit % divisor == 0)
break;
}
if(divisor > sqrt(digit))
printf("%d\n",digit);
}
return 0;
}
判断质数
题目要求:对于给定的一个大于 1 的正整数 N,判定它是否是一个质数
我们输入一个整数,如果要判断它不是一个质数,我们可以列出某一个范围的整数,通过求余数的方式判断这些列出的数字是不是给出整数的因数。如果发现任何一个因数,我们就可以确认它不是质数,输出 NO
后直接 break
就可以了(不用再判断后续的整数了)。如果所有的列出的数都被判断不是它的因数,则可以最后输出 YES
。
#include <stdio.h>
#include <math.h>
int main() {
int digit;
int divisor;
int flag;
scanf("%d", &digit);
if (digit < 2)
{
printf("NO\n");
return 0;
}
if (digit == 2)
{
printf("YES\n");
return 0;
}
for (divisor = 2; divisor <= sqrt(digit); divisor ++)
{
if (digit % divisor == 0)
{
flag = 0;
break;
}
if(divisor > sqrt(digit))
{
flag = 1;
}
}
if(flag == 1)
printf("YES\n");
if(flag == 0)
printf("NO\n");
return 0;
}
小于指定值的质数
题目要求:请对于给定的一个大于 1的正整数 N,按从小到大的顺序输出所有小于等于它的质数
思路与输出质数相似,将第一层循环的上限改为输入即可。
#include <stdio.h>
#include <math.h>
int main() {
int digit;
int divisor;
int num;
scanf("%d", &num);
printf("2\n");
if(num > 2)
{
for (digit = 3; digit <= num; digit += 2)
{
for (divisor = 2; divisor < sqrt(digit); divisor ++)
{
if (digit % divisor == 0)
break;
}
if(divisor > sqrt(digit))
printf("%d\n",digit);
}
}
return 0;
}
指定范围的质数
题目要求:请对于给定的一个大于1的正整数N和一个大于1的正整数M,N一定大于M(N,M均小于等于10的6次方),请按从小到大的顺序输出所有小于等于N且大于等于M的质数
这里我们采用质数筛法(sieve method)的方式。与之前的对每一个数字依次判断是否为质数的方式不同,筛法的思想是“标记出所有非质数,输出所有没被标记的数字”。这里以“输出15以内大于1的所有质数”为例,示例代码如下:
#include <stdio.h>
int main() {
int n = 15;
int mark[16] = {
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
int c;
int j;
for (c = 2; c * c <= n; c++) {
if (mark[c] != 1) {
for (j = 2; j <= n / c; j++) {
mark[c * j] = 1;
}
}
}
for (c = 2; c <= n; c++) {
if (mark[c] != 1) {
printf("%d\n", c);
}
}
return 0;
}
这里采用此筛法,并使用string.h中的memset函数设置数组内的元素。本题代码如下:
#include <stdio.h>
#include <string.h>
int n = 1000000;
int mark[1000001];
int main() {
int c;
int i, j;
int N;
int M;
scanf("%d%d", &N, &M);
memset(mark, 0, sizeof(mark));
mark[0] = 1;
mark[1] = 1;
for (c = 2; c * c <= n; c++) {
if (mark[c] != 1) {
for (i = 2; i <= N / c; i++) {
mark[c * i] = 1;
}
}
}
for (j = M; j <= N; j++) {
if (mark[j] != 1) {
printf("%d\n",j);
}
}
return 0;
}
总结
有关质数的问题(判断质数、打印质数、统计质数个数等)我们可以采用筛法减少复杂度(有一种埃氏筛法可以减少时间复杂度,可以了解)。可以将判断质数功能独立为质数检验函数。当然,检验范围缩小至2到平方根可以大大减小算法的复杂度。