1. 什么是素数
素数(又称质数)是指大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的数。例如 2、3、5、7、11 都是素数,而 4、6、8、9 都不是素数。
判断一个数是否为素数,是 C 语言初学者最经典的练习题目之一,也是后续学习算法优化、数论的基础。
2. 最基础的判断方法
最直观的思路:对于一个数 n,从 2 开始一直试除到 n-1,如果中间存在某个数能整除 n,则 n 不是素数;否则 n 是素数。
c
#include <stdio.h>
int isPrime(int n) {
if (n <= 1) {
return 0; // 1 和负数不是素数
}
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return 0; // 能被整除,不是素数
}
}
return 1; // 是素数
}
int main() {
int num;
printf("请输入一个整数:");
scanf("%d", &num);
if (isPrime(num)) {
printf("%d 是素数\n", num);
} else {
printf("%d 不是素数\n", num);
}
return 0;
}
这种方法虽然正确,但效率较低。当 n 很大时,循环次数接近 n 次,时间复杂度为 O(n)。
3. 优化一:只需判断到 √n
观察可以发现:如果 n 有一个大于 √n 的因数 a,那么必然存在一个小于 √n 的因数 b = n / a。因此,我们只需要从 2 试除到 √n 即可。
c
#include <stdio.h>
#include <math.h>
int isPrime(int n) {
if (n <= 1) {
return 0;
}
for (int i = 2; i <= sqrt(n); i++) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
这样时间复杂度降为 O(√n),性能大幅提升。
4. 优化二:跳过偶数
除了 2 以外,所有偶数都不是素数。因此可以先单独判断 2,然后从 3 开始只检查奇数。
c
int isPrime(int n) {
if (n <= 1) {
return 0;
}
if (n == 2) {
return 1; // 2 是素数
}
if (n % 2 == 0) {
return 0; // 偶数不是素数
}
for (int i = 3; i <= sqrt(n); i += 2) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
这样循环次数又减少了一半,效率进一步提升。
5. 综合示例:输出 1~100 之间的所有素数
下面把上面的优化综合起来,输出 1 到 100 之间的所有素数:
c
#include <stdio.h>
#include <math.h>
int isPrime(int n) {
if (n <= 1) {
return 0;
}
if (n == 2) {
return 1;
}
if (n % 2 == 0) {
return 0;
}
for (int i = 3; i <= sqrt(n); i += 2) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
int main() {
printf("1~100 之间的素数有:\n");
for (int i = 1; i <= 100; i++) {
if (isPrime(i)) {
printf("%d ", i);
}
}
printf("\n");
return 0;
}
运行结果:
1~100 之间的素数有:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
6. 进阶:埃拉托斯特尼筛法
当需要一次性判断大量数字(比如求 1 到 1000000 之间所有素数)时,逐个判断效率太低。此时可以使用埃拉托斯特尼筛法(Sieve of Eratosthenes),时间复杂度为 O(n log log n)。
基本思想:从 2 开始,把每个素数的倍数都标记为合数,剩下的就是素数。
c
#include <stdio.h>
#include <stdbool.h>
void sieve(int n) {
bool isPrime[n + 1];
for (int i = 0; i <= n; i++) {
isPrime[i] = true;
}
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= n; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
printf("1~%d 之间的素数有:\n", n);
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
printf("%d ", i);
}
}
printf("\n");
}
int main() {
sieve(100);
return 0;
}
7. 总结
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| 基础试除法 | O(n) | 小数字、教学演示 |
| 试除到 √n | O(√n) | 单个数字判断 |
| 跳过偶数优化 | O(√n/2) | 单个数字判断(推荐) |
| 埃拉托斯特尼筛法 | O(n log log n) | 批量判断大量数字 |
掌握素数的判断方法,不仅能帮助你通过 C 语言的基础练习,更是理解算法复杂度优化的重要一步。建议初学者先掌握基础写法,再逐步理解优化思路,最后尝试用筛法解决更大规模的问题。