什么意思?
将规定范围内的数从大到小依次排列,从2开始依次筛去该数的倍数,因为素数的倍数一定不是素数,筛完后剩下的数即为素数。
eg:求1~100范围的素数,先去掉2的倍数,再去掉3的倍数,再去掉5的倍数…….,直到最后一个数,剩下的就是素数。
如何编程实现?
根据初始数据是从大到小依次排列的整数,可考虑设一个数组,数组下标代表需判断的数,数组的值代表该数是否为素数,
若为素数则数组项赋值为1,否则为0。用循环赋值下标为合数的数组项为0筛去合数,输出值为1的数组项的下标,即为所求素数。
可否优化?
可。某些数被重复判断筛选,降低运行效率。
eg:30=215=310=5*6,30作为2、3、5、6、15的倍数会被重复筛去。
怎样优化?
线性筛法:让每一个合数被它最小的质因数筛到。由于每个合数最小的质因数是唯一的,由不同的最小质因数筛到的合数也不同,这样就不会重复筛到同一个合数。
演示:eg 求1~12之间的素数
2 3 4 5 6 7 8 9 10 11 12
primes:()
从头到尾遍历,第一个数是2,未被划掉,把它放进质数表:
2 3 4 5 6 7 8 9 10 11 12
primes:(2,)
用2去乘质数表里的每个数,划掉它们:
2 3 4 5 6 7 8 9 10 11 12
下一个是3,加入质数表,划掉6、9:
2 3 4 5 6 7 8 9 10 11 12
primes:(2,3,)
下一个是4(注意这里划掉的数也要遍历,只是不加入质数表),先划掉8,但我们不划掉12,因为12 (12=26=34) 应该由它的最小质因数2筛掉,而不是3。
2 3 4 5 6 7 8 9 10 11 12
primes:(2,3,)
下一个是5,加入质数表,划掉10:
2 3 4 5 6 7 8 9 10 11 12
primes:(2,3,5)
……..
规律:对于一个数k,筛去k乘以primes数表里的数(从第一个开始)得到的合数,直到k可以整除primes中的某个数
按这样的步骤进行下去,可以筛掉所有的合数,并得到一张质数表。
代码(普通筛):
#include <stdio.h>
#define MAX 100
int main(void)
{
int i, j;
int isPrime[MAX + 1];
for (i = 2; i < MAX + 1; i++) //将所有数组项赋值为1,所有数默认为素数
isPrime[i] = 1;
for (j = 2; j * j < MAX; j++) //将下标为合数的数组项赋值0,筛去合数
for (i = j; i < MAX + 1; i++)
if (!(i % j))
isPrime[i] = 0;
for (i = 2; i < MAX + 1; i++) //输出值为1的数组项下标,即为素数
if (isPrime[i] == 1)
printf("%d ", i);
return 0;
}
优化后(线性筛):
#include <stdio.h>
#define MAX 100
int main(void)
{
int i, j, count = 0;
int isPrime[MAX + 1], primes[MAX + 1]={0};
for (i = 0; i < MAX + 1; i++) //将所有isPrime数组项赋值为1,默认所有数均为素数
isPrime[i] = 1;
for (i = 2; i < MAX + 1;i++)
{
if(isPrime[i]) //将确认的素数存入primes数组
primes[count++] = i;
for (j = 0; j <= count && primes[j] * i <= MAX;j++) //用线性筛法筛去合数
{
isPrime[i * primes[j]] = 0;
if(i%primes[j]==0)
break;
}
}
for (i = 0; i <count;i++) //输出数组primes,即输出所求的所有素数
printf("%d ", primes[i]);
return 0;
}