【算法氵】筛法

埃拉托斯特尼筛法:

质朴的算法:

  确认质数:从2开始,从小到大找第一个未被筛去的数

  筛去合数:确认是质数之后,筛去质数的所有倍数

因此程序大概就是

埃拉托斯特尼筛

记得小学课本学“素数”的时候就有类似的图:

  一张10*10的数表,从2开始,从小到大找第一个未被划斜杠的数,之后将其的倍数全部杠掉

 (缺图)

很快就会发现,2筛去了一半的数,而之后的偶数几乎都被筛去了很多次(类似于鞭尸。。),也就是说,花费了很多无用的时间来筛去已经被筛过的数

于是我们很容易就会想到这样的优化:当当前”筛子“不为2时,每次隔1个数筛一个

以此类推,隔2个隔3个……

于是最终就可以迭代到欧拉筛法:

欧拉筛法:

  “思想是 使每个数只被其最小质因数所筛去 ”

讲起来简单,但却不好实现

一种实现的办法是:

  之前的埃筛法中,只有素数可以当筛子,因为判定素数和筛除操作是一起执行的

  我们知道,所谓合数,也就是可以表达成素数幂之积

  如:6174=2*3*3*7*7*7

  按照欧拉筛法的思想,应该使得6174被2筛去,但是如果合数也可以做筛子:

    那么可以让6174被3087筛去

    3087被1029筛去

    1029被343筛去

    343被49筛去

    49被7筛去

    7没有被比它小的数所筛去,可以确定7是素数

  也就是说,如果每个素数筛去 该素数与 不大于它的素数 之积,合数筛去 该合数与 不大于它最小质因子 的素数 之积,就可以不重复不遗漏(类似于数学归纳法)

举例:

  2做筛子,筛去4

  3做筛子,筛去6,9

  4做筛子,筛去8

  5做筛子,筛去10,15,25

  6做筛子,筛去12

  7做筛子,筛去14,21,35,49

  8做筛子,筛去16

  9做筛子,筛去18,27

  10做筛子,筛去20

  11做筛子,筛去22,33,55,77,121

  12做筛子,筛去24

  13做筛子,筛去26,39,65,91,143,169

  14做筛子,筛去28

  15做筛子,筛去30,45

  16做筛子,筛去32

  17做筛子,筛去34,51,85,119,187,221,289

……大概就是这样子

因此:

  确认质数:从2开始,从小到大找第一个未被筛去的数

  筛去合数:每个数都筛去 它与 不大于它的素数 之积,如果这个数是合数,则发现其第一个因子的时候停止筛除操作

 代码如下

 1 for(int i=2; i<=MAX; ++i) {
 2     if(!number[i])
 3         prime[++ctprime]=i;
 4     else
 5         for(int ii=1; i*prime[ii]<=MAX && ii<=ctprime; ++ii) {
 6             number[i*prime[ii]]=true;
 7             if(i%prime[ii]==0)
 8                 break;
 9         }
10 }
11 // bool number[MAX]= {1,1,0};//0为素数,MAX为数据规模
12 // int prime[]= {0,2};//这个数组可以比MAX小很多
13 // int ctprime;//初始值为0
线性筛
 1 for(int i=2; i<=MAX; ++i) {
 2         if(!number[i]) {//优化:素数一定可以筛到它的平方,减少判断
 3             prime[++ctprime]=i;
 4             for(int ii=1; ii<=ctprime && i*prime[ii]<=MAX; ++ii)
 5                 number[i*prime[ii]]=true;
 6         } else
 7             for(int ii=1; i*prime[ii]<=MAX && ii<=ctprime; ++ii){
 8                 number[i*prime[ii]]=true;
 9                 if(i%prime[ii]==0)
10                     break;
11             }
12     }
稍微优化一下的版本

OVER

  

  

原文地址:https://www.cnblogs.com/MirrozSigmaMax/p/13917140.html