zoukankan      html  css  js  c++  java
  • 数学素数筛及其拓展

    以前写筛法都这么写:

    埃拉托斯尼斯筛法

     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);




  • 相关阅读:
    Redis
    双向绑定篇
    Vue篇1
    css篇-页面布局-三栏布局
    css篇-简化版
    Promise篇
    几道JS代码手写面试题
    安全篇
    Vue篇
    跨域篇--JSONP原理
  • 原文地址:https://www.cnblogs.com/DragoonKiller/p/4295954.html
Copyright © 2011-2022 走看看