求素数? 试试埃拉托斯特尼筛法和线性素数筛

判断素数

如果是在小范围内如1到200,使用普通判断素数的方法就行了
C语言经典之判断素数
严格的说这只是挨个判断一个区间内的数是否是素数,不能算是筛选出素数,当区间范围很大.这种方法花费的时间是无法忍受的

埃拉托斯特尼筛法

如何求一段区间内的素数呢?我们可以将这个区间内的合数完全剔除掉,
注意一个合数C(composite)总能写成一个素数P(prime)和其他任意数O(other)的乘积
C=PO C = P ∗ O
例如要求出[2,25]内的素数
我们先找出最小的素数 2
将2组成的合数剔除掉,例如
2*2__2*3__2*4__2*5 ……
留下来的序列是: 2__3__5__7__9__11__13__15__17__19__21__23__25
留下最近的一个未被剔除的数(肯定是素数) 是 3
那么我们再将3组合成的合数剔除掉
3*2__3*3__3*4__3*5 ……

筛选到什么时候结束呢?
第一种方案 很自然地想到筛选到最后一个数为止
其实不必这么麻烦,

例如当我们将上面这个序列筛到5的时候
序列变成2__3__5__7__11__13__17__19__23
我们考察序列中 最大的一个数的开方
23 √ 23 若假设23可以被某两个数 d1,d2 d 1 , d 2 整除,那么这两个数分布肯定是这样的
这里写图片描述

回过头来,我们已经提出了2,3,5所有的合数,也就是
C=2O C = 2 ∗ O , C=3O C = 3 ∗ O , C=5O C = 5 ∗ O
而最大的数23若能被 d1,d2 d 1 , d 2 整除
就可以写成
23=d1O 23 = d 1 ∗ O
那么 d1<23<5 d 1 < √ 23 < 5
d1<5 d 1 < 5 的话肯定就会被 2,3,5组成的合数剔除掉
换句话说
23要是合数,肯定会被√23之前的某个数找到,如果√23之前都找不到,那么√23以后的数就不用找了.
而且
序列中最大的数23都找不到了,那么比23还小的数(13__17__19)就肯定也找不到了了

综上所述,整个递增序列只需找到 n √ n 即可

#include<stdio.h>
#include<math.h>
#define range 99999
int main()
{
    int i;
    int j;
    int check[range] = { 0 };// 0代表是素数 
    for (i = 2; i*i< range; i++)//C=P*O;sqrt range 左边筛完 右边就肯定找不到了
    {
        if (!check[i])//如果是素数才进行剔除它组成的合数
        {
            for (j = 2 * i; j < range; j += i)
            {
                check[j] = 1;//剔除素数组成的合数
            }
        }
    }
    for (i = 2; i < range; i++)
    {
        if (check[i] == 0)//打印留下来的素数
        {
            printf("%d ", i);
        }
    }
    getchar();
}

这个方法有个坏处是某些合数会被重复的剔除
例如6 会被 23,32 2 ∗ 3 , 3 ∗ 2 剔除两次

这里写图片描述
避免这种情况,可以改进为线性素数筛

线性素数筛

#include<stdio.h>
#include<time.h>
int main()
{
    int i;
    int j;
    int prime[range] = { 0 };
    int check[range] = { 0 };

    int primes = 0;
    for (i = 2; i < range; i++)
    {
        if (!check[i])
        {
            prime[primes++] = i;
        }

        for (j = 0; j < primes && i*prime[j] < range; j++)
        {
            check[i*prime[j]] = 1;//合数可以化为合数and素数的乘积
            if (!i%prime[j])//储存在prime[j]中的素数都是升序的 
            {
                break;
            }
        }
    }

这里还是利用了 C=iP C = i ∗ P 这一公式
即:合数=任意一数✖
素数
但是保证不重复筛选,我们可以把公式写成
C=iPmin C = i ∗ P m i n
即此合数=任意一数*最小素因子
例如:12=4*3
我们可以写成12=6*2;
看程序
当 i=4的时候
会筛掉 4*2,
由于 i%prime[j]是4%2=0了;
所以此时并不会执行4*3

12这个合数只会在
12=6(i)*2(最小素因子)的时候被剔除;

我们得出了所有合数都只会在最小素因子下被剔除

C=IP C = I ∗ P
P已经确定,那么I不同,肯定对应不同的C

所以不同的合数只会被剔除一次

对于质数不存在 C=iP C = i ∗ P 的关系

所以肯定会被保留下来.
为什么i%prime[j]可以保证上述情况呢

将i 写成 i=pmina i = p m i n ∗ a 的形式
若 i%prime[j] ==0;

pminaprime[j+1] p m i n ∗ a ∗ p r i m e [ j + 1 ]
可以写成
aprime[j+1]pmin a ∗ p r i m e [ j + 1 ] ∗ p m i n
显然 pmin<prime[j+1] p m i n < p r i m e [ j + 1 ]
也就是刚才那个例子
12=34=3(22) 12 = 3 ∗ 4 = 3 ∗ ( 2 ∗ 2 )
总要化成
12=(32)2=62 12 = ( 3 ∗ 2 ) ∗ 2 = 6 ∗ 2 才对


参考:

https://www.jianshu.com/p/f16d318efe9b
http://blog.csdn.net/u014427196/article/details/44466461

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值