😁博客主页😁:🚀从0至1-优快云博客🚀
🤑博客内容🤑:🍭C语言、C++、数据结构、嵌入式、Linux🍭
😎本文内容🤣:🍭BC127 筛选法求素数🍭
😎金句分享😎:🍭No🍭
目录
描述
用筛选法求n以内的素数。筛选法求解过程为:将2~n之间的正整数放在数组内存储,将数组中2之后的所有能被2整除的数清0,再将3之后的所有能被3整除的数清0 ,以此类推,直到n为止。数组中不为0 的数即为素数。
输入描述:
多组输入,每行输入一个正整数(不大于100)。
输出描述:
针对每行输入的整数n,输出两行,第一行,输出n之内(包括n)的素数,用空格分隔,
第二行,输出数组中2之后被清0 的个数。每行输出后换行。
示例1
首先要理解题目意思
任务:使用筛选法(也称为埃拉托斯特尼筛法)找出给定范围内的所有素数。
筛选法过程:
- 初始化数组:将2到n之间的所有正整数存储在一个数组中。
- 标记非素数:
- 从2开始,将数组中2之后的所有能被2整除的数标记为0。
- 然后从3开始,将数组中3之后的所有能被3整除的数标记为0。
- 依次类推,直到处理完所有小于等于n的数。
- 结果:数组中不为0的数即为素数。
输入描述:
- 多组输入,每行输入一个正整数(不大于100)。
输出描述:
- 对于每行输入的整数n,输出两行:
- 第一行:输出n之内(包括n)的所有素数,用空格分隔。
- 第二行:输出数组中2之后被清0的个数。
示例
假设输入如下:
10
15
预期输出:
2 3 5 7
6
2 3 5 7 11 13
8
解释示例
输入 10
-
初始化数组:
- 数组
arr
初始化为:{0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10}
- 数组
-
筛选过程:
- 当
p = 2
时:- 标记
4
,6
,8
,10
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 9, 0}
- 标记
- 当
p = 3
时:- 标记
9
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0}
- 标记
- 当
-
输出结果:
- 素数:
2 3 5 7
- 被清0的个数:
6
- 素数:
输入 15
-
初始化数组:
- 数组
arr
初始化为:{0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
- 数组
-
筛选过程:
- 当
p = 2
时:- 标记
4
,6
,8
,10
,12
,14
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 9, 0, 11, 0, 13, 0, 15}
- 标记
- 当
p = 3
时:- 标记
9
,15
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0, 11, 0, 13, 0, 0}
- 标记
- 当
p = 5
时:- 标记
10
已经是0,不需要再次标记。 - 数组保持不变:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0, 11, 0, 13, 0, 0}
- 标记
- 当
-
输出结果:
- 素数:
2 3 5 7 11 13
- 被清0的个数:
8
- 素数:
关键点总结
- 初始化数组:将2到n之间的所有正整数存储在数组中。
- 标记非素数:逐步将每个素数的倍数标记为0。
- 输出结果:
- 输出数组中不为0的数(即素数)。
- 统计并输出在2之后被清0的数的个数。
解题思路
-
输入处理:
- 读取一个正整数
n
(不大于100)。
- 读取一个正整数
-
初始化数组:
- 创建一个大小为100的数组
arr
,并将所有元素初始化为0。 - 将数组索引从2到
n
的位置分别赋值为相应的整数。
- 创建一个大小为100的数组
-
筛选过程:
- 使用双重循环来标记非素数:
- 外层循环从2开始遍历到
n
,每次迭代时j
表示当前正在检查的数。 - 内层循环从
j+1
开始遍历到n
,每次迭代时i
表示当前要检查的数。 - 如果
arr[i]
能被j
整除,则将arr[i]
置为0,表示arr[i]
不是素数。
- 外层循环从2开始遍历到
- 使用双重循环来标记非素数:
-
统计和输出结果:
- 遍历数组,输出所有不为0的数(即素数),并用空格分隔。
- 统计并输出在2之后被清0的数的个数。
步骤详解
-
输入处理:
- 使用
scanf
函数从标准输入读取一个整数,并存储在变量n
中。
- 使用
-
初始化数组:
- 创建一个大小为100的整数数组
arr
,并将所有元素初始化为0。 - 使用
for
循环将数组arr
的索引从2到n
的位置分别赋值为相应的整数。
- 创建一个大小为100的整数数组
-
筛选过程:
- 使用外层循环从2开始遍历到
n
,每次迭代时j
表示当前正在检查的数。 - 使用内层循环从
j+1
开始遍历到n
,每次迭代时i
表示当前要检查的数。 - 如果
arr[i]
能被j
整除(即arr[i] % j == 0
),则将arr[i]
置为0,并增加计数器j
。
- 使用外层循环从2开始遍历到
-
输出结果:
- 使用
for
循环遍历数组,输出所有不为0的数(即素数),并用空格分隔。 - 输出在2之后被清0的数的个数。
- 使用
代码
#include <stdio.h>
int main()
{
int n, i, j;
int arr[100] = {0}; // 设初始数组元素为100个0
scanf("%d", &n); // 读取输入的整数n
// 初始化数组,将数组索引从2到n的位置分别赋值为相应的整数
for (i = 2; i <= n; i++)
arr[i] = i; // 为了方便,设数组下标和数组元素一样,从2到n
// 筛选过程
for (j = 2; j <= n; j++) // 外循环为2到n,即被2到n的数整除
{
for (i = j + 1; i <= n; i++) // 内循环为遍历数组进行比较,每趟循环的数组下标从j+1开始
{
if (arr[i] % j == 0) // 若能被j(从2到n)整除
arr[i] = 0; // 还原为0
}
}
// 遍历数组,输出未被清0的数(即素数)
for (i = 2, j = 0; i <= n; i++) // 遍历数组,
{
if (arr[i] != 0)
printf("%d ", arr[i]); // 未被清0的数打印出来
else
j++; // 清0的数,计数+1
}
printf("\n"); // 换行
// 打印被清0的数的个数
printf("%d", j); // 打印被清0的数的个数
return 0;
}
代码解释
#include <stdio.h>
int main()
{
int n, i, j;
int arr[100] = {0}; // 设初始数组元素为100个0
scanf("%d", &n); // 读取输入的整数n
// 初始化数组,将数组索引从2到n的位置分别赋值为相应的整数
for (i = 2; i <= n; i++)
arr[i] = i; // 为了方便,设数组下标和数组元素一样,从2到n
// 筛选过程
for (j = 2; j <= n; j++) // 外循环为2到n,即被2到n的数整除
{
for (i = j + 1; i <= n; i++) // 内循环为遍历数组进行比较,每趟循环的数组下标从j+1开始
{
if (arr[i] % j == 0) // 若能被j(从2到n)整除
arr[i] = 0; // 还原为0
}
}
// 遍历数组,输出未被清0的数(即素数)
for (i = 2, j = 0; i <= n; i++) // 遍历数组,
{
if (arr[i] != 0)
printf("%d ", arr[i]); // 未被清0的数打印出来
else
j++; // 清0的数,计数+1
}
printf("\n"); // 换行
// 打印被清0的数的个数
printf("%d", j); // 打印被清0的数的个数
return 0;
}
详细步骤解释
-
输入处理:
scanf("%d", &n);
- 使用
scanf
函数从标准输入读取一个整数n
。
- 使用
-
初始化数组:
for (i = 2; i <= n; i++) arr[i] = i;
- 使用
for
循环将数组arr
的索引从2到n
的位置分别赋值为相应的整数。 - 例如,如果
n
是5,则数组arr
的前几个元素会被设置为:arr[2] = 2
,arr[3] = 3
,arr[4] = 4
,arr[5] = 5
。
- 使用
-
筛选过程:
for (j = 2; j <= n; j++) // 外循环为2到n,即被2到n的数整除 { for (i = j + 1; i <= n; i++) // 内循环为遍历数组进行比较,每趟循环的数组下标从j+1开始 { if (arr[i] % j == 0) // 若能被j(从2到n)整除 arr[i] = 0; // 还原为0 } }
- 外层循环 (
for (j = 2; j <= n; j++)
):- 从2开始遍历到
n
,每次迭代时j
表示当前正在检查的数。
- 从2开始遍历到
- 内层循环 (
for (i = j + 1; i <= n; i++)
):- 从
j+1
开始遍历到n
,每次迭代时i
表示当前要检查的数。 - 如果
arr[i]
能被j
整除(即arr[i] % j == 0
),则将arr[i]
置为0,表示arr[i]
不是素数。
- 从
- 外层循环 (
-
输出结果:
for (i = 2, j = 0; i <= n; i++) // 遍历数组, { if (arr[i] != 0) printf("%d ", arr[i]); // 未被清0的数打印出来 else j++; // 清0的数,计数+1 } printf("\n"); // 换行 printf("%d", j); // 打印被清0的数的个数
- 初始化计数器 (
j = 0
):- 使用
j
计数被清0的数的个数。
- 使用
- 遍历数组 (
for (i = 2, j = 0; i <= n; i++)
):- 从2开始遍历到
n
,每次迭代时i
表示当前检查的数。 - 如果
arr[i]
不为0,则打印该数(即素数)。 - 如果
arr[i]
为0,则增加计数器j
,表示有一个数被清0。
- 从2开始遍历到
- 换行 (
printf("\n")
):- 在输出完素数后换行。
- 输出被清0的数的个数 (
printf("%d", j)
):- 打印在2之后被清0的数的个数。
- 初始化计数器 (
示例
假设输入的 n
是10,那么这段代码的工作过程如下:
-
初始化数组:
for (i = 2; i <= n; i++) arr[i] = i;
- 数组
arr
初始化为:{0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10}
- 数组
-
筛选过程:
-
当
j = 2
时:for (i = 3; i <= 10; i++) { if (arr[i] % 2 == 0) arr[i] = 0; }
- 标记
4
,6
,8
,10
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 9, 0}
- 标记
-
当
j = 3
时:for (i = 4; i <= 10; i++) { if (arr[i] % 3 == 0) arr[i] = 0; }
- 标记
9
为0。 - 数组变为:
{0, 0, 2, 3, 0, 5, 0, 7, 0, 0, 0}
- 标记
-
当
j = 4
到j = 10
时:- 没有新的数被标记为0,因为之前已经被标记过的数不会再被标记。
-
-
输出结果:
- 素数:
2 3 5 7
- 被清0的个数:
6
- 素数:
扩展
拓展1:多组输入处理
需求:
- 处理多组输入,每行输入一个正整数(不大于100),并针对每组输入输出相应的结果。
示例:
输入:
10
15
输出:
2 3 5 7
6
2 3 5 7 11 13
8
实现:
- 使用
while
循环不断读取输入,直到遇到文件结束符(EOF)。
多组输入处理的筛选法求n以内的素数
#include <stdio.h>
int main() {
int n, i, j;
int arr[100] = {0}; // 设初始数组元素为100个0
// 使用 while 循环不断读取输入,直到遇到文件结束符(EOF)
while (scanf("%d", &n) != EOF && n > 0) {
if (n > 100) {
printf("Input should be less than or equal to 100.\n");
continue;
}
// 初始化数组,将数组索引从2到n的位置分别赋值为相应的整数
for (i = 2; i <= n; i++)
arr[i] = i;
// 筛选过程
for (j = 2; j * j <= n; j++) { // 外循环为2到sqrt(n),即被2到sqrt(n)的数整除
if (arr[j] != 0) { // 如果arr[j]不为0,说明j是素数
for (i = j * j; i <= n; i += j) { // 内循环为遍历数组进行比较,每趟循环的数组下标从j*j开始
if (arr[i] != 0) { // 若能被j整除
arr[i] = 0; // 还原为0
}
}
}
}
// 统计被清0的数的个数
int count_cleared = 0;
for (i = 2; i <= n; i++) {
if (arr[i] == 0)
count_cleared++;
}
// 输出素数
for (i = 2; i <= n; i++) {
if (arr[i] != 0)
printf("%d ", arr[i]); // 未被清0的数打印出来
}
printf("\n");
// 打印被清0的数的个数
printf("%d\n", count_cleared);
}
return 0;
}
拓展2:优化筛选法
需求:
- 优化筛选法,减少不必要的计算。
实现:
- 只需要检查到
sqrt(n)
即可。 - 使用布尔数组来标记素数,避免使用整数数组。
#include <stdio.h>
#include <stdbool.h>
void sieveOfEratosthenes(int n) {
bool is_prime[n + 1]; // 布尔数组,初始化为true,表示所有数都是素数
for (int i = 0; i <= n; i++) {
is_prime[i] = true;
}
is_prime[0] = is_prime[1] = false; // 0和1不是素数
int count_cleared = 0; // 记录被清0的数的个数
for (int p = 2; p * p <= n; p++) { // 外循环为2到sqrt(n),即被2到sqrt(n)的数整除
if (is_prime[p]) { // 如果p是素数
for (int j = p * p; j <= n; j += p) { // 内循环为遍历数组进行比较,每趟循环的数组下标从p*p开始
if (is_prime[j]) { // 若j是素数
is_prime[j] = false; // 标记j为非素数
count_cleared++; // 增加被清0的计数
}
}
}
}
// 输出素数
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
printf("%d ", i); // 未被清0的数打印出来
}
}
printf("\n");
// 输出被清0的数的个数
printf("%d\n", count_cleared);
}
int main() {
int n;
while (scanf("%d", &n) != EOF && n > 0) { // 使用 while 循环不断读取输入,直到遇到文件结束符(EOF)
if (n > 100) {
printf("Input should be less than or equal to 100.\n");
continue;
}
sieveOfEratosthenes(n); // 调用筛选函数
}
return 0;
}
拓展3:统计不同范围内的素数
需求:
- 输入多个范围,每个范围由两个整数
a
和b
表示,输出每个范围内(包括a
和b
)的所有素数及其个数。
示例:
输入:
10 20
25 30
输出:
11 13 17 19
4
29
1
实现:
- 读取多个范围
[a, b]
,并对每个范围应用筛选法。
#include <stdio.h>
#include <stdbool.h>
void sieveInRange(int a, int b) {
if (b < 2) {
printf("No prime numbers in range [%d, %d]\n", a, b);
printf("0\n");
return;
}
bool is_prime[b + 1]; // 布尔数组,初始化为true,表示所有数都是素数
for (int i = 0; i <= b; i++) {
is_prime[i] = true;
}
is_prime[0] = is_prime[1] = false; // 0和1不是素数
for (int p = 2; p * p <= b; p++) { // 外循环为2到sqrt(b),即被2到sqrt(b)的数整除
if (is_prime[p]) { // 如果p是素数
for (int j = p * p; j <= b; j += p) { // 内循环为遍历数组进行比较,每趟循环的数组下标从p*p开始
if (is_prime[j]) { // 若j是素数
is_prime[j] = false; // 标记j为非素数
}
}
}
}
int count_primes = 0; // 记录素数的个数
for (int i = a; i <= b; i++) {
if (is_prime[i]) {
printf("%d ", i); // 未被清0的数打印出来
count_primes++;
}
}
printf("\n");
// 输出素数的个数
printf("%d\n", count_primes);
}
int main() {
int a, b;
while (scanf("%d %d", &a, &b) != EOF) { // 使用 while 循环不断读取输入,直到遇到文件结束符(EOF)
if (a > b || a < 0 || b > 100) {
printf("Invalid range. Ensure 0 <= a <= b <= 100.\n");
continue;
}
sieveInRange(a, b); // 调用筛选函数
}
return 0;
}
拓展4:输出最大素数和最小素数
需求:
- 对于每个输入的
n
,输出n
以内的最大素数和最小素数。
示例:
输入:
10
15
输出:
2 7
2 13
实现:
- 在筛选过程中记录最大和最小的素数。
#include <stdio.h>
#include <stdbool.h>
void sieveAndFindExtremes(int n) {
if (n < 2) {
printf("No prime numbers in range [2, %d]\n", n);
return;
}
bool is_prime[n + 1]; // 布尔数组,初始化为true,表示所有数都是素数
for (int i = 0; i <= n; i++) {
is_prime[i] = true;
}
is_prime[0] = is_prime[1] = false; // 0和1不是素数
for (int p = 2; p * p <= n; p++) { // 外循环为2到sqrt(n),即被2到sqrt(n)的数整除
if (is_prime[p]) { // 如果p是素数
for (int j = p * p; j <= n; j += p) { // 内循环为遍历数组进行比较,每趟循环的数组下标从p*p开始
if (is_prime[j]) { // 若j是素数
is_prime[j] = false; // 标记j为非素数
}
}
}
}
int min_prime = -1; // 记录最小素数
int max_prime = -1; // 记录最大素数
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
if (min_prime == -1) {
min_prime = i; // 设置第一个找到的素数为最小素数
}
max_prime = i; // 更新最大素数
}
}
if (min_prime == -1) {
printf("No prime numbers in range [2, %d]\n", n);
} else {
printf("%d %d\n", min_prime, max_prime); // 输出最小和最大素数
}
}
int main() {
int n;
while (scanf("%d", &n) != EOF && n > 0) { // 使用 while 循环不断读取输入,直到遇到文件结束符(EOF)
if (n > 100) {
printf("Input should be less than or equal to 100.\n");
continue;
}
sieveAndFindExtremes(n); // 调用筛选函数
}
return 0;
}