zoukankan      html  css  js  c++  java
  • 「算法笔记」线性筛

    一、Etratosthenes 筛法

    任意整数 (x) 的倍数 (2x,3x,cdots) 都不是质数。考虑从 (2) 开始,由小到大扫描每个数 (x),把它的倍数 (2x,3x,cdots,lfloor frac{n}{x} floor imes x) 标记为合数。当扫描到一个数时,若它尚未被标记,则它不能被 (2sim x-1) 之间的任何数整除,该数就是质数。

    另外,可以发现,(2)(3) 都会把 (6) 标记为合数。实际上,小于 (x^2)(x) 的倍数在扫描更小的数时就已经被标记过了。可以对 Etratosthenes 筛法进行优化,对于每个数 (x),只需要从 (x^2) 开始,把 (x^2,(x+1) imes x,cdots,lfloor frac{n}{x} floor imes x) 标记为合数即可。

    memset(vis,0,sizeof(vis));    //合数标记 
    for(int i=2;i<=n;i++){
        if(vis[i]) continue;
        p[++cnt]=i;    //i 是质数 
        for(int j=i;j<=n/i;j++) vis[i*j]=1;
    }

    时间复杂度:(O(nlog log n))

    二、线性筛法

    可以发现,即使在优化后(从 (x^2) 开始),Eratosthenes 筛法仍然会重复标记合数。如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 (O(n)) 了。

    每个数被它最小的质因子筛一次。

    vis[0]=vis[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    } 
    • 每个数被它的最小质因子筛掉。每次筛去 (i imes p[j])(p[j]) 是这个数的最小质因子。
    • 如果 (imid p[j]) 说明 (i imes p[j]) 有两个 (p[j]) 的质因子,并且 (p[j])(i imes p[j]) 的最小质因子。若再枚举,则 (p[j]) 就不是 (i imes p[j]) 的最小质因子了。所以筛过一次 (p[j]) 就 break。

    三、筛积性函数

    1. 一些定义

    数论函数是指一个正整数到整数的映射。

    积性函数:对于所有 互质 的整数 (a,b),有性质 (f(ab)=f(a)f(b)) 的数论函数。(f(1)=1)

    完全积性函数:对于所有整数 (a,b),有性质 (f(ab)=f(a)f(b)) 的数论函数。

    2. 常见的积性函数

    约数个数函数  (d(n)=sum_{d|n} 1)

    约数和函数  (sigma (n)=sum_{d|n} d)

    约数 (k) 次幂函数  (sigma _k (n)=sum_{d|n} d^k)

    欧拉函数  (varphi (n)=sum_{i=1}^n [gcd(i,n)=1])

    莫比乌斯函数  (mu (n)=egin{cases}1&{n=1}\(-1)^k&c_{1,2,...,k}=1 (n=prod_{i=1}^k p_i^{c_i})\0&c_i>1end{cases})

    四、常见线性筛

    1. 求约数和

    给定 (n),求 (f(1),f(2)...f(n))。其中 (f(x)=sum_{d|x} d)

    证明约数和函数是积性函数:

    考虑 (a,b),并且 (gcd(a,b)=1,ab=x)

    (f(a)f(b)=sum_{dmid a}dsum_{pmid b}p=sum_{dmid a}sum_{pmid b}dp=sum_{(dp)mid (ab)} dp=sum_{tmid x}t=f(x))

    其中,因为 (gcd(a,b)=1),则 (gcd(d,p)=1)

    线性筛求约数和函数:

    根据唯一分解定理,可得:(n=p_1^{c_1} imes p_2^{c_2} imes cdots imes p_k^{c_k}=prodlimits_{i=1}^k p_i^{c_i})

    (sigma (x)=(1+p_1+p_1^2+cdots+p_1^{c_1}) imes (1+p_2+p_2^2+cdots p_2^{c_2}) imes cdots imes (1+p_k+p_k^2cdots p_k^{c_k}))

    (sigma (x)=prodlimits_{i=1}^k sumlimits_{j=0}^{c_i} p_i^j)。其中 (sigma (x)) 表示 (x) 的约数和。

    (f_i) 表示 (i) 的约数和,(g_i) 表示 (1+p+p^2+cdots +p^c),其中 (p) 表示 (i) 的最小质因子。

    g[1]=f[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) p[++cnt]=i,g[i]=f[i]=i+1;    //当 i 是质数时,显然有 g[i]=f[i]=i+1
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            int x=i*p[j];
            if(i%p[j]==0){
                g[x]=g[i]*p[j]+1,f[x]=f[i]/g[i]*g[x];    //多了一个因子,也就是 1+p^1+p^2+...+p^c 变成了 1+p^1+p^2+...+p^{c+1} 了,那么更新 g 只需要将所有的乘上 p 再 +1 就好了。 
                break;
            }
            else f[x]=f[i]*f[p[j]],g[x]=1+p[j];    //新的因子(i*p[j] 里原先没有 p[j] 这一项) 。p[j] 是 i*p[j] 的最小质因子。 
        }
    }
    for(int i=1;i<=n;i++) f[i]=f[i-1]+f[i];

    2. 求欧拉函数

    对于正整数 (n),欧拉函数是小于或等于 (n) 的正整数中与 (n) 互质的数的数目,记作 (varphi(n))

    (varphi(n)=sum_{i=1}^n [gcd(i,n)=1])

    ([a]):如果 (a) 为真,则 ([a]) 的值为 (1);否则为 (0)

    (n=prod_{i=1}^k{p_i}^{c_i})(p_i) 是质数)。

    公式:(varphi(n)=nprod_{i=1}^k(1-frac{1}{p_i}))

    vis[0]=vis[1]=1,phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) p[++cnt]=i,phi[i]=i-1;    //当 i 为质数时,显然有 phi[i]=i-1。 
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];    //i 和 i*p[j] 都包含 p[j] 这个质因子。将 phi[i*p[j]] 和 phi[i] 按照公式写出,二者相除,商为 p[j]。 
                break;
            }
            phi[i*p[j]]=phi[i]*phi[p[j]];    //i 和 p[j] 互质。利用积性函数的性质。 
        }
    }

    3. 求莫比乌斯函数

    (p_i) 是质数。(mu (n)=egin{cases}1&{n=1}\(-1)^k&c_{1,2,...,k}=1 (n=prod_{i=1}^k p_i^{c_i})\0&c_i>1end{cases})

    证明莫比乌斯函数是积性函数:

    对于 (forall a,b),并且 (gcd(a,b)=1,ab=x)(mu(a)mu(b))

    1. 若 (mu(a)=0)(mu(b)=0),则 (ab) 必含平方因子(即存在 (c_i>1))。所以 (mu(a,b)=0)

    2. 否则,由于 (gcd(a,b)=1),则 (ab) 不含平方因子。则 (mu(a)mu(b)=(-1)^{k_1}(-1)^{k_2}=(-1)^{k_1+k_2}=mu(ab))

    vis[0]=vis[1]=1,u[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) p[++cnt]=i,u[i]=-1;    //当 i 为质数时,此时 k=1 且 c1=1,显然有 u[i]=-1 
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){u[i*p[j]]=0;break;}    //多了一个因子。此时 i*p[j] 必含平方因子,所以 u[i*p[j]]=0。 
            u[i*p[j]]=-u[i];    //新的因子。 
        }
    }

    4. 求约数个数

    根据唯一分解定理,可得:(n=p_1^{c_1} imes p_2^{c_2} imes cdots imes p_k^{c_k}=prodlimits_{i=1}^kp_i^{c_i})

    (p_i^{c_i}) 的约数有 (p_i^0,p_i^1,cdots,p_i^{c_i})(c_i+1) 个,根据乘法原理,可得 (n) 的约数个数为 (d(n)=prodlimits_{i=1}^k (c_i+1))

    (d_i) 表示 (i) 的约数个数,(g_i) 表示 (i) 的最小质因数的次数。

    vis[0]=vis[1]=1,d[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]) p[++cnt]=i,d[i]=2,g[i]=1;    //若 i 为质数,显然有 d[i]=2,g[i]=1 
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                d[i*p[j]]=d[i]/(g[i]+1)*(g[i]+2);    //多了一个因子。并且 p[j] 是 i*p[j] 的最小质因子。d[i*p[j]]=(1+c1+1)(1+c2)...(1+ck)。 
                g[i*p[j]]=g[i]+1; 
                break;
            }
            d[i*p[j]]=d[i]*d[p[j]],g[i*p[j]]=1;    //新的因子。i*p[j] 之前不包含 p[j]。p[j] 是 i*p[j] 的最小质因子。 
        }
    }

    四、一般线性筛

    一个积性函数 (f),可以考虑以下的线性筛法:

    • 要计算 (f(n)),考虑到 (n=prodlimits_{i=1}^k p_i^{c_i})

    • 在线性筛的过程中,如果 (imod p[j] eq 0),就有 (f(icdot p[j])=f(i)f(p[j]))

    • 否则,考虑 (kmid (icdot p[j]),gcd(k,p[j])=1),则有 (f(icdot p[j])=f(k)f(frac{icdot p[j]}{k}))

    • 因此,我们只需要记录对于每一个 (icdot p[j]) 对应的 (k),或者推导出 (f(p^c))(f(p^{c+1})) 的关系式即可。

  • 相关阅读:
    说说我当初是如何学Linux的
    案例八:shell自动化管理账本脚本
    案例七:shell实现开机自动播放挂载本地yum仓库程序
    案例六:shell脚本监控httpd服务80端口状态
    案例五:shell脚本实现定时监控http服务的运行状态
    案例四:Shell脚本生成随机密码
    案例三:shell统计ip访问情况并分析访问日志
    案例二:shell脚本获取当前日期和时间及磁盘使情况
    案例一:shell脚本指定日期减去一天
    Linux:保证数据安全落盘
  • 原文地址:https://www.cnblogs.com/maoyiting/p/12654723.html
Copyright © 2011-2022 走看看