数学-素数筛及其拓展

以前写筛法都这么写:

埃拉托斯尼斯筛法

 

 1 void getprime(int n)
 2 {
 3     for(int i=2;i<=n;i++)
 4     if(!v[i])
 5     {
 6         prime[ptot++]=v[i];
 7         for(int j=i<<1;j<=n;j+=i)
 8         v[j]=true;
 9     }
10 }

 


复杂度O(nlglgn).

 

实测n=1kW耗时0.18s ; n=1E耗时2.24s.


然后这里是欧拉线性筛

 

 1 void getprime(int n)
 2 {
 3     for(int i=2;i<=n;i++)
 4     {
 5         if(!v[i]) prime[ptot++]=i;
 6         int k;
 7         for(int j=0;j<ptot && ((k=i*prime[j])<=n);j++)
 8         {    
 9             v[k]=true;
10             if(i%prime[j]==0) break;
11         }
12     }
13 }

 

复杂度O(n).

 

实测n=1kW耗时0.1s,n=1E耗时1.02s.

看得出,尽管有乘除操作,但是线性筛在十万以上的n,速度就已经超越了普通筛法.

所以在n<=20W的范围上可以直接用埃拉托斯尼斯筛法,更大的话就用线性筛加速.


证明大概是说明两点,一是所有合数都被标记了,二是所有数最多被标记一次.证明就不多说了.....


然后,就是素数筛求积性函数.用这个做法是因为线性筛把所有数的最小素因子求出来了.

比如求莫比乌斯函数....

 

 1 void getmou(int n)
 2 {
 3     mou[1]=1;
 4     for(int i=2;i<=n;i++)
 5     {
 6         if(!v[i]) prime[ptot++]=i,mou[i]=-1;
 7         int k;
 8         for(int j=0;j<ptot && ((k=i*prime[j])<=n);j++)
 9         {    
10             v[k]=true;
11             mou[k]=-mou[i];
12             if(i%prime[j]==0)
13             {
14                 mou[k]=0;
15                 break;
16             }
17         }
18     }
19 }

 

大概意思是,这样的:

 

首先素数的函数值就是-1.

然后,我们在筛合数的时候,是i*prime[j]的形式,而prime[j]是i*prime[j]的最小素因子.

如果这个合数不存在平方因子,那么根据莫比乌斯函数的定义,mou[i*prime[j]]=-mou[i];

如果这个合数存在平方因子,那么 mou[i*prime[j]]=0;

至于直接写mou[k]=-mou[i],是因为如果

1.i本身就有平方因子了,那么明显mou[i]=0,于是mou[k]=0=-mou[i];

2.i在乘上prime[j]后才出现平方因子,这说明prime[j]是平方因子,prime[j]整除i,

所以要对prime[j]整除i的情况特判,让mou[i*prime[j]]=0.


线性筛还能快速求欧拉函数表.

 

 1 void geteular(int n)
 2 {
 3     eu[1]=1;
 4     for(int i=2;i<=n;i++)
 5     {
 6         if(!v[i]) prime[ptot++]=i,eu[i]=i-1;        
 7         int k;
 8         for(int j=0;j<ptot && (k=i*prime[j])<=n ; j++)
 9         {
10             v[k]=true;
11             eu[k]=eu[i]*(prime[j]-(i%prime[j]!=0));
12             if(i%prime[j]==0) break;
13         }
14     }
15 }

 

 

根据欧拉函数的公式$$\phi(n)= \prod_{i=1}^{m}{p_i^{a_i-1}(p_i-1)} = \prod_{p|n}p^{\alpha_p-1}(p-1) = n\prod_{p|n}(1-\frac{1}{p}) $$

可以推出递推式

eu[i*prime[j]]=eu[i]*(prime[j]-(i%prime[j]!=0));

 


因数个数函数表(叫做τ(n)或者σ0(n))

 1 void gen(int n)
 2 {
 3     cnt[1]=fct[1]=1;
 4     for(int i=2;i<=n;i++)
 5     {
 6         if(v[i]==false) prime[ptot++]=i,cnt[i]=2,fct[i]=1;
 7         int k;
 8         for(int j=0;j<ptot && (k=i*prime[j])<=n; j++)
 9         {
10             v[k]=true;
11             if(i%prime[j]==0)
12             {
13                 fct[k]=fct[i]+1;
14                 cnt[k]=cnt[i]/(fct[i]+1)*(fct[i]+2);
15                 break;
16             }
17             fct[k]=1;
18             cnt[k]=cnt[i]*2;
19         }
20     }
21 }

 

为了根据公式$$ \tau(n)=\sum{(a_1+1)(a_2+1)...(a_p+1)}, \\ n=2^{a_1}3^{a_2}5^{a_3}7^{a_4}...P^{a_p} $$计算出它的值,需要额外开一个数组fct存储"i的因数分解中,i的最小质因数有多少次幂".然后根据上边套式子.......

式中求结果的函数也可以写成

cnt[k]=cnt[i]+cnt[i]/(fct[i]+1);




 

posted @ 2014-11-29 15:23  DragoonKiller  阅读(133)  评论(0编辑  收藏  举报