厄拉多塞筛法
原理:
厄拉多塞筛法(Eratosthenes Sieve)是一种求素数的方法,也称之为标记法,由古希腊数学家厄拉多塞提出。 它的原理是:给定一个数 n,从 2 开始依次将 n 以内所有的数放入一个足够大的数组中(此时数组的所有赋值均为0),给数组中下标为素数的倍数数组值标记(1),标记完成后,剩余未被标记的数为素数(即数组素数下标里的值为0),此时我们只需要输出数组值为0的数组的下标就能得到2~n以内的素数。(素数是从 2 开始的,除了1和本身没有其他的公因数的数)。
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... | n |
分析:
(1)第一个素数为2,此时我们只需要找到所有下标为2的倍数时数组的值,然后直接将此数组中的值赋值为1;此时一轮循环下去之后我们就能将2~n以内的数直接排除掉一半;
0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... | n |
(2)第二个素数为3,此时我们同样只需要找到下标为3的倍数时数组的值,然后直接然后直接将此数组中的值赋值为1;此时又一轮下去直接减点原来的三分之一。
0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... | n |
(3)然后依次下去最终能够排除将数组下标为合数的值直接标记为1,剩下数组中标记为0的数组下标就是素数;
....
代码:
#include<stdio.h>
int main() {//厄拉多塞筛法。
int n;
scanf("%d", &n);
int nums[1001] = { 0 };//定义标记数组
for (int i = 2; i <= n; i++) {
if (nums[i] == 0) {//是素数就给标记为0,不是给标记为1
/*printf("%d\n", i);*/
//对每个素数的倍数进行寻找排除,当i=2时(2为素数),这时排除所有是2的倍数的数字,
//i++之后3为素数,然后再排除为3的倍数的数字;
//.......
//在这里使用了加号是因为乘号要比加号在计算机中复杂。
for (int j = i + i; j <= n; j += i) {
nums[j] = 1;
}
}
}
for (int i = 2; i <=n; i++) {
if (nums[i] == 0) {
printf("%d ", i);
}
}
return 0;
}
此时任意输入一个数n即可得到n以内的所有素数。
时间复杂度分析与对比:
一般而言我们首先想到的是双重for循环来计算素数问题,但是对于厄拉多塞筛法而言,虽然里面出现了二重循环,但是时间复杂度远远比二重循环下的要低,
一般做法时间复杂度:
我们用一个数count来计算for循环里面执行的次数来求得一般做法时间复杂度。
#include<stdio.h>
int main()
{
int N, j,count=0;
scanf("%d", &N);
for (int i = 2; i <= N; i++)
{
for (j = 2; j < i; j++) {
count++;
if (i % j == 0) {
count++;
break;
}
}
if (i == j) {
//printf("%d ", i);
}
}
printf("\n");
printf("执行次数为:");
printf("%d\n", count);
printf("\n");
return 0;
}
执行结果:
取n=1000时,此时执行次数为78853,此时时间复杂度非常之高,
厄拉多塞筛法时间复杂度:
同样用一个count来计算for循环里面执行的次数。
代码:
#include<stdio.h>
int main() {//厄拉多塞筛法。
int n;
scanf("%d", &n);
int count = 0;
int nums[1001] = { 0 };//定义标记数组
for (int i = 2; i <= n; i++) {
if (nums[i] == 0) {//是素数就给标记为0,不是给标记为1
/*printf("%d\n", i);*/
//对每个素数的倍数进行寻找排除,当i=2时(2为素数),这时排除所有是2的倍数的数字,
//i++之后3为素数,然后再排除为3的倍数的数字;
//.......
for (int j = i + i; j <= n; j += i) {
nums[j] = 1;
count++;
}
}
}
/*for (int i = 2; i <= n; i++) {
if (nums[i] == 0) {
printf("%d ", i);
}
}*/
printf("\n");
printf("执行次数为:");
printf("%d", count);
return 0;
}
执行结果:
此时我们能看出当n=100时执行次数只有1958次,这就差不多相当于是O(n)的时间复杂度了
通过两种方法的对比我们可以发现厄拉多塞筛法的高明之处,在时间复杂度上厄拉多塞筛法能够大大减少程序的时间复杂度。