线性筛

来源:互联网 发布:淘宝虚假交易处罚规则 编辑:程序博客网 时间:2024/06/11 17:07

线性筛用在素数、欧拉函数、莫比乌斯函数的打表上。

埃拉托斯特尼筛法

一开始最容易理解的筛法是酱紫滴~

#define N 1000100#define LL long longint num[N], prim[N];int pn = 0;void table(){    memset(num, -1, sizeof(num));    for(int i = 2;i < N;i++) if(num[i]){        prim[pn++] = i;        for(LL j = 1LL*i*i;j < N;j += i) if(num[j])            num[j] = 0;    }}

注意在标记i的倍数只需从i*i开始,因为小于它的i的倍数势必已经被更小的数作为倍数筛掉。同时小心i*iint。但是这个写法会让一些数被重复筛。如30,会被2、3、5作为倍数筛三遍,这样的访问太多余了。

欧拉筛法

这个筛法做到了每个数只被筛一遍。

#define N 100100000#define LL long longint num[N], prim[N];int pn = 0;void table(){    memset(num, -1, sizeof(num));    for(int i = 2;i < N;i++){        if(num[i]) prim[pn++] = i;        for(int j = 0;j < pn && 1LL*i*prim[j] < N;j++){            num[i*prim[j]] = 0;            if(i % prim[j] == 0) break;        }    }}

全篇的精华在于:

if(i % prim[j] == 0) break;

这个break保证了合数只被最小质约数访问到。
比如40=2*20=4*10=5*8,只有i=20时,才会在prim[j]=2的时候被访问到。
i=10时,在prim[j]=2时就已经被break了;同样的,i=8时,在prim[j]也已经break了。
敢于这样break的原因在于,当i % prim[j] == 0(prim[j])2n,则在i=nprim[j]时,自然会与prim[j]共同来标记n(prim[j])2,而且仅有最小的prim[j]会标记它,这样就保证了不重不漏。

附赠打表

来优雅地打个表

不,是下面这个…

充分利用了欧拉函数、莫比乌斯函数作为积性函数的性质,灰常好写~

#define N 100100#define LL long longint num[N], prim[N], phi[N] = {1,1}, mob[N]={1,1};int pn = 0;void table(){    memset(num, -1, sizeof(num));    for(int i = 2;i < N;i++){        if(num[i]) {            prim[pn++] = i;            phi[i] = i-1;            mob[i] = -1;        }        for(int j = 0;j < pn && 1LL*i*prim[j] < N;j++){            if(i % prim[j] == 0){                phi[i*prim[j]] = phi[i] * prim[j];                num[i*prim[j]] = 0;                mob[i*prim[j]] = 0;                break;            }            phi[i*prim[j]] = phi[i] * (prim[j]-1);            num[i*prim[j]] = 0;            mob[i*prim[j]] = -mob[i];        }    }}
0 0
原创粉丝点击