跳过正文
  1. Docs/

筛法

·1356 字·3 分钟· loading · loading · · ·
Algorithms Prime C/C++
Pacyu
作者
Pacyu
程序员
目录

这里介绍几种快速求素数的算法。

Sieve of Eratosthenes (埃氏筛)
#

时间复杂度:\( O(nlglgn) \)

埃氏筛 - 维基百科

#include <bits/stdc++.h>
using namespace std;
const int N = 2000010;
int main(int n, char**v)
{
    bool prime[N];
    for(int i = 2; i < N; i++)
        prime[i] = true;
    for (int i = 2; i <= sqrt(N); i++)
        if (prime[i])
            for (int j = i*i; j < N; j += i)
                prime[j] = false;
    for (int i = 2; i < N; i++)
        if (prime[i])
            cout << i << ' ';
    return 0;
}

Sieve of Atkin
#

参见: Sieve of Atkin - wikipedia

Sieve of prime (素数筛)
#

学会这个筛法就可以A掉这个题了:P3383 【模板】线性筛

#include <bits/stdc++.h>
using namespace std;
#define rg register
const int N = 10000010;
int prime[N], notprime[N];
void SievePrime()
{
    int step = 0;
    notprime[1] = 1;
    for (rg int i = 2; i < N; i++)
    {
        if (!notprime[i]) prime[++step] = i;
        for (rg int j = 1; j <= step && prime[j] * i < N; j++)
        {
            notprime[i * prime[j]] = 1;
            if (i%prime[j] == 0) break;
        }
    }
}
int main()
{
    SievePrime();
    for (rg int i = 0; i < N; i++)
        if (prime[i])
            cout << prime[i] << ' ';
    return 0;
}

Sieve of Euler (欧拉筛)
#

原理
#

欧拉函数
#

\( \phi(n) = n \prod_{p|n} (1 - \frac{1}{p}) = \prod(p - 1)p^{k_r - 1} \)

其中 \( p \) 为 \( n \) 的所有质因子。它表示在不超过 \( n \) 的正整数中与 \( n \) 互质的数的个数。

如果 \( p \) 是素数,\( k \geq 1 \),那么有

\( \phi(p^k) = p^{k - 1}(p - 1) = p^k(1 - \frac{1}{p}) \)

利用这个公式便可以证明欧拉函数。

注意欧拉函数是 积性函数,这个性质很重要。

首先 \( n = p_1^{k_1} \cdots p_r^{k_r} \),则

$$ \begin{aligned} \phi(n) &= \phi(p_1^{k_1})\phi(p_2^{k_2}) \cdots \phi(p_r^{k_r}) \\ &= p_1^{k_1}(1 - \frac{1}{p_1})p_2^{k_2}(1 - \frac{1}{p_2}) \cdots p_r^{k_r}(1 - \frac{1}{p_r}) \\ &= p_1^{k_1}p_2^{k_2} \cdots p_r^{k_r}(1 - \frac{1}{p_1})(1 - \frac{1}{p_2}) \cdots (1 - \frac{1}{p_r}) \\ &= n(1 - \frac{1}{p_1})(1 - \frac{1}{p_2}) \cdots (1 - \frac{1}{p_r}) \end{aligned} $$

  • 与欧拉定理、费马小定理的关系

对任何两个互质的正整数 \( a, m (m >= 2) \),有欧拉定理 \( a^{\phi(m)} = 1(mod \ m) \) (其中 \( \phi(m) \) 是欧拉函数) ,当 \(( m \) 是质数 \ p \) 时,式子变为

\( x^{p - 1} = 1(mod \ p) \)

即费马小定理。

更多关于欧拉函数,另请参见:

  1. 欧拉函数 - 百度百科
  2. Euler function - wikipedia

求欧拉函数的算法
#

#include <bits/stdc++.h>
using namespace std;
int eular(int n)
{
    int ret=1;
    for(int i = 2; i*i <= n; i++)
    {
        if(n%i == 0)
        {
            n /= i,ret *= i - 1;
            while(n%i == 0) n /= i, ret *= i;
        }
    }
    if(n > 1) ret *= n - 1;
    return ret;
}
int main()
{
    int n, s;
    cin >> n;
    s = eular(n);
    cout << s << endl;
    return 0;
}

欧拉筛法实现
#

#include <bits/stdc++.h>
using namespace std;
#define rg register
const int N = 10000010;
int prime[N], notprime[N], phi[N];
void SieveEuler()
{
    int step = 0;
    phi[1] = 1;
    for (rg int i = 2; i < N; i++)
    {
        if (!notprime[i]) prime[++step] = i, phi[i] = i - 1;
        for (rg int j = 1; j <= step && prime[j] * i < N; j++)
        {
            notprime[i * prime[j]] = 1;
            if (i%prime[j] == 0)
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            else phi[i * prime[j]] = phi[i]*(prime[j] - 1);
        }
    }
}
int main()
{
    SieveEuler();
    for (rg int i = 0; i < N; i++)
        if (prime[i])
            cout << prime[i] << ' ';
    return 0;
}

Sieve of Mobius (莫比乌斯筛)
#

原理
#

莫比乌斯函数
#

\( \mu(n) = \eta_{\omega(n)}^{\Omega(n)}\lambda(n) \)

这里 \( \lambda(n) \) 是刘维尔函数。

莫比乌斯函数的定义:

For any positive integer n, define μ(n) as the sum of the primitive nth roots of unity. It has values in {−1, 0, 1} depending on the factorization of n into prime factors:

  • μ(n) = 1 if n is a square-free positive integer with an even number of prime factors.
  • μ(n) = −1 if n is a square-free positive integer with an odd number of prime factors.
  • μ(n) = 0 if n has a squared prime factor.

莫比乌斯函数完整定义的通俗表达:

  1. 莫比乌斯函数 \( \mu(n) \) 的定义域是 \( N \);
  2. \( \mu(1) = 1 \);
  3. 当\( n \)存在平方素因子时,\( \mu(n) = 0 \);
  4. 当\( n \)是素数或奇数个不同素因子之积时,\( \mu(n) = -1 \);
  5. 当\( n \)是偶数个不同素因子之积时,\( \mu(n) = 1 \).

$$ \mu(n) = \left( \begin{aligned} & 1 \quad n = 1; \\ & (-1)^k \quad n 是无平方因数且 n = p_1 p_2 \cdots p_k \\ & 0 \quad n 有大于 1 的平方因数. \end{aligned} \right. $$

莫比乌斯函数也是一个积性函数(\( \mu(ab) = \mu(a)\mu(b) \),\( a \) 和 \( b \) 互质 )

  • 当 \( n = 1 \) 时,\( n \) 的所有因子的莫比乌斯函数值和为 \( 1 \);
  • 当 \( n \neq 1 \) 时,\( n \) 的所有因子的莫比乌斯函数值和为 \( 0 \).

$$ \sum_{d|n}\mu(d) = \left( \begin{aligned} 1 \quad n = 1; \\ 0 \quad n > 1. \end{aligned} \right. $$

参考:

  1. 莫比乌斯函数 - 百度百科
  2. Mobius function - wikipedia

莫比乌斯筛法实现
#

#include <bits/stdc++.h>
using namespace std;
#define rg register
const int N = 1000010;
int prime[N], notprime[N], mobius[N];
void SieveMobius()
{
    int step = 0;
    mobius[1] = 1;
    for (rg int i = 2; i < N; i++)
    {
        if (!notprime[i]) prime[++step] = i, mobius[i] = -1;
        for (rg int j = 1; j <= step && prime[j] * i < N; j++)
        {
            notprime[i * prime[j]] = 1;
            if (i%prime[j] == 0)
            {
                mobius[i * prime[j]] = 0;
                break;
            }
            else mobius[i * prime[j]] = -mobius[i];
        }
    }
}
int main()
{
    SieveMobius();
    for (int i = 0; i < N; i++)
        if (prime[i])
            cout << prime[i] << ' ';
    return 0;
}

其他参考
#

  1. 线性筛
  2. 几种线性筛

相关文章

计数排序
·174 字·1 分钟· loading · loading
Algorithms C/C++ Sorting
简介 # 计数排序是一种空间换时间的排序算法。通常用作 基数排序的
矩阵运算
·4964 字·10 分钟· loading · loading
Algorithms Mathematical Math C/C++
这篇主要介绍一些矩阵运算相关算法。 矩阵乘法 # 矩阵相乘:\( C
Kruskal's algorithm
·456 字·1 分钟· loading · loading
Algorithms C/C++
Kruskal算法可用来求解最小生成树(minimum-sp
Tarjan's algorithm
·950 字·2 分钟· loading · loading
Algorithms C/C++
Tarjan算法可以用来求有向图的强连通分量个数。算法由Ro
拓扑排序
·681 字·2 分钟· loading · loading
Algorithms C/C++
生活中许多实际应用都需要使用有向无环图来指明事件的优先次序。
Kosaraju's algorithm
·890 字·2 分钟· loading · loading
Algorithms C/C++
该算法可以用来求解一个有向图的强连通分量。 算法解析 # 什么是强