首页 > 其他分享 >筛法

筛法

时间:2024-07-24 09:31:56浏览次数:7  
标签:lfloor right 筛法 dfrac rfloor sqrt left

筛法,可能是数论算法中最古老、最有活力、最奇妙、最引人入胜的部分了。

埃拉托斯特尼筛法

Sift the Two's and Sift the Three's:
The Sieve of Eratosthenes.
When the multiples sublime,
The numbers that remain are Prime.

—— Anonymous

埃拉托斯特尼筛,简称埃氏筛,它从 \(2\) 开始迭代地不断将每个素数地倍数标记为合数,剩下的自然都是素数。

由于这个过程很像一个筛子不断筛掉合数,留下质数,这种算法被称为“筛法”。

由于埃氏筛会重复标记合数,它的时间复杂度略高于线性,为 \(O(n\log\log n)\)。

略证

操作数组的次数为

\[O\left(\sum_{k=1}^{\pi(n)}\dfrac{n}{p_k}\right) \]

只需证

\[\sum_{k=1}^{\pi(n)}\dfrac{1}{p_k}=O(\log\log n) \]

由 \(\pi(n)=O\left(\dfrac{n}{\log n}\right)\),\(p_k=O(k\log k)\),

\[\begin{aligned} \sum_{k=1}^{\pi(n)}\dfrac{1}{p_k} &=O\left(\sum_{k=2}^{\pi(n)}\dfrac{1}{k\log k}\right)\\ &=O\left(\int_{2}^{\pi(n)}\dfrac{\text{d}x}{x\log x}\right)\\ &=O(\log\log\pi(n))=O(\log\log n)\\ &&\square \end{aligned} \]

我们可以每次标记合数时从 \(p^2\) 开始,这实际上不会改变渐进,但是实践上是重要的常数优化。

更进一步地,若使用 C++ STL 中地 bitset 作为标记数组,在使用 O2 优化的前提下,可以在 1s 筛出 \(2^{30}\) 以内的质数。

bitset<N> vis;
void sieve() {
  vis[0] = vis[1] = true;
  for (int i = 2; i * i < N; ++i)
    if (!vis[i])
      for (int j = i * i; j < N; j += i)
        vis[j] = true;
}

上述代码预处理了 vis 数组,被标记为 true 意味着这一位不是质数。

可以 \(O(n)\) 再扫一遍 vis 数组,将 \(n\) 以内质数存储到另一个数组中。

欧拉筛

欧拉筛是埃氏筛的改进,可以做到 \(O(n)\) 筛出 \(n\) 以内的全体质数,但其常数略大,筛选质数的性能比不上埃氏筛,常被用于预处理积性函数。

具体来说,欧拉筛保证每个合数被它最小的质因数标记。

为了做到这一点,欧拉筛改变了标记质数时的枚举顺序。埃氏筛对于每个质数枚举倍数,而欧拉筛对每个倍数枚举质数:

bitset<N> vis;
vector<int> prime;
void sieve() {
  vis[0] = vis[1] = true;
  for (int i = 2; i < N; ++i) {
    if (!vis[i]) prime.push_back(i);
    for (int j: prime) {
      vis[i * j] = true;
      if (i % j == 0) break;
    }
  }
}

可以看到,函数体内,保证了 i * j 的最小质因子为 j

注意欧拉筛过程中可以得到每个数的最小质因子。

欧拉筛求积性函数

由于欧拉筛在标记每个合数时确保 j 是其最小质因子,非常方便在欧拉筛的过程中计算积性函数。

有些函数可以很方便的直接计算,如:

  • 欧拉函数:\(\varphi(ij)=\varphi(i)\times j\)(\(i,j\) 含义与上述代码一致,且 \(j\mid i\),下同)
  • 莫比乌斯函数:\(\mu(ij)=0\)

也有些函数需要额外维护数组,如:

  • 约数个数函数,额外维护 \(n(i)\) 表示 \(i\) 中最小质因子的数量,那么:
    \(n(ij)=n(i)+1,d(ij)=\dfrac{d(i)}{n(ij)}\cdot(n(ij)+1)\)

总之,若积性函数 \(f\) 可以 \(O(1)\) 计算 \(f(p^k)\)(至少可以 \(O(1)\) 递推出来),就可以在 \(O(n)\) 时间筛出 \(f\) 在 \(n\) 以内的点值。

这个方法有一个没大有用的扩展,可以做到 \(O\left(n+\dfrac{n\log k}{\log n}\right)\) 预处理,\(O(1)\) 查询 \(n\) 以内数的 \(k\) 次方,这里不展开说明了。

筛法意义的扩展

原因已难以考证,总之,在中国 OI 界,我们将一切关于“前 \(n\) 项”的数论求和问题都归为筛法。

有许多巧妙的和式处理技巧可以在亚线性时间内求数论函数的前缀和,这些方法被称之为“亚线性筛”。

记号说明

下文中:

  • 字母 \(p\) 默认取值为质数集 \(\mathbf{P}\)
  • 对于小写字母表示的数论函数,用大写字母表示其前缀和函数 i.e. \(F(n)=\sum_{i=1}^nf(i)\)
  • 符号 \(\ast\) 表示狄利克雷卷积 i.e. \((f\ast g)(n)=\sum_{d\mid n}f(d)g\left(\dfrac{n}{d}\right)\)
  • 下文的亚线性筛介绍中,以求 \(F(n)\) 为目标。

数论分块

尽管好像没人说这是“筛”,由于它也用于快速计算和式,也将其放在这里介绍。

假设可以 \(O(1)\) 计算或已经预处理了 \(f\) 的前缀和,数论分块可以在 \(O(\sqrt{n})\) 的复杂度内计算 \(\sum_{i=1}^{n}f(i)g\left(\left\lfloor\dfrac{n}{i}\right\rfloor\right)\)。

数论分块基于这样一个事实:\(\left\lfloor\dfrac{n}{i}\right\rfloor\) 的值在 \(i\) 接近时可能是相等的,如果我们把相等的部分提出来计算,可以减小 \(i\) 的枚举次数。

数论分块结论:值 \(\left\lfloor\dfrac{n}{i}\right\rfloor\) 所在块的右端点为 \(\left\lfloor\dfrac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)。

略证

令 \(k=\left\lfloor\dfrac{n}{i}\right\rfloor\),则 \(k\le\dfrac{n}{i}\),于是

\[\left\lfloor\dfrac{n}{k}\right\rfloor\ge\left\lfloor\dfrac{n}{\frac{n}{i}}\right\rfloor=i \]

故 \(i_{\max}=\left\lfloor\dfrac{n}{k}\right\rfloor=\left\lfloor\dfrac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)

时间复杂度

引理 \(1\):

\[\forall n\in\mathbf{N_{+}},\left|\left\{\left\lfloor\dfrac{n}{d}\right\rfloor\mid d \in \mathbf{N_{+}},d\leq n \right\}\right|\le\lfloor2\sqrt{n}\rfloor \]

略证
  • 对于 \(d\le\lfloor\sqrt{n}\rfloor\),\(\left\lfloor\dfrac{n}{d}\right\rfloor\) 有 \(\lfloor\sqrt{n}\rfloor\) 个不同取值;
  • 对于 \(d\gt\lfloor\sqrt{n}\rfloor\),\(\left\lfloor\dfrac{n}{d}\right\rfloor\le\lfloor\sqrt{n}\rfloor\),也只有 \(\lfloor\sqrt{n}\rfloor\) 个不同取值。

由引理 \(1\),易知时间复杂度为 \(O(\sqrt{n})\)

更进一步地,如果是数论分块套数论分块,时间复杂度为 \(O(n^{3/4})\)。

略证

考虑数论分块的过程,

  • 当 \(d\le \sqrt{n}\) 时,最多 \(\sqrt{n}\) 个块,套数论分块的复杂度是:

\[O\left(\sum_{i=1}^{\lfloor\sqrt{n}\rfloor}\sqrt{\dfrac{n}{i}}\right)=O\left(n\int_{1}^{\sqrt{n}}\dfrac{\text{d}x}{\sqrt{x}}\right)=O(n^{3/4}) \]

  • 当 \(d>\sqrt{n}\) 时,同样最多 \(\sqrt{n}\) 个块,每块长 \(O(\sqrt{n})\),套数论分块的复杂度是:

\[O\left(\sqrt{n}\sqrt{\sqrt{n}}\right)=O(n^{3/4}) \]

故总的时间复杂度也是 \(O(n^{3/4})\)。

杜教筛

由某位知名的杜姓 OI 选手引入中国 OI 界,据说是在 PE 论坛上看到的。

设法构造 \(F(n)\) 的递推式,引入另一数论函数 \(g\):

\[\begin{aligned} \sum_{i=1}^{n}(f\ast g)(n) &=\sum_{i=1}^{n}\sum_{d\mid i}g(d)f\left(\dfrac{i}{d}\right)\\ &=\sum_{i=1}^{n}\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}g(i)f(j)\\ &=\sum_{i=1}^{n}g(i)\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}f(j)\\ &=\sum_{i=1}^{n}g(i)F\left(\left\lfloor\dfrac{n}{i}\right\rfloor\right) \end{aligned} \]

移项得

\[g(1)F(n)=\sum_{i=1}^{n}(f\ast g)(n)-\sum_{i=2}^{n}g(i)F\left(\left\lfloor\dfrac{n}{i}\right\rfloor\right) \]

假设我们构造的函数 \(g\) 满足

  1. 可以快速计算 \(\sum_{i=1}^{n}(f\ast g)(n)\)
  2. 可以快速计算 \(g\) 的前缀和

那么,我们可以在短时间内计算 \(F(n)\)。

注意:

  1. 无论 \(f\) 是否是积性函数,只需构造适当的数论函数 \(g\),即可应用杜教筛。
  2. 可以用杜教筛求 \((f\ast g)\) 和 \(g\) 的前缀和,复杂度不变,但是常数增大。多套几层就比不上线性筛了。

时间复杂度

引理 \(2\):

\[\forall a,b,c\in\mathbf{Z},\left\lfloor\dfrac{a}{bc}\right\rfloor=\left\lfloor\dfrac{\left\lfloor\frac{a}{b}\right\rfloor}{c}\right\rfloor \]

略证

\[\begin{aligned} &\frac{a}{b}=\left\lfloor\frac{a}{b}\right\rfloor+r(0\leq r<1)\\ \implies &\left\lfloor\frac{a}{bc}\right\rfloor =\left\lfloor\frac{a}{b}\cdot\frac{1}{c}\right\rfloor =\left\lfloor \frac{1}{c}\left(\left\lfloor\frac{a}{b}\right\rfloor+r\right)\right\rfloor =\left\lfloor \frac{\left\lfloor\frac{a}{b}\right\rfloor}{c} +\frac{r}{c}\right\rfloor =\left\lfloor \frac{\left\lfloor\frac{a}{b}\right\rfloor}{c}\right\rfloor\\ &&\square \end{aligned} \]

引理 \(3\):令 \(R(n)=\left\{\left\lfloor\dfrac{n}{k}\right\rfloor:k=2,3,\cdots n\right\}\),则对于任意 \(m\in R(n)\),\(R(m)\subseteq R(n)\)。

由引理 \(2\),结论显然,不证。

设计算 \(f\ast g\) 和 \(g\) 的前缀和的复杂度均为 \(O(1)\),由引理 \(3\),每个 \(F(k)(k\in R(n))\) 均只计算 \(1\) 次。

由引理 \(2\),设计算 \(F(n)\) 的时间复杂度为 \(T(n)\)。

\[\begin{aligned} T(n) &=\sum_{k\in R(n)}T(k)\\ &=\sum_{k=1}^{\lfloor\sqrt{n}\rfloor}O(\sqrt{k})+\sum_{k=2}^{\lfloor\sqrt{n}\rfloor}O\left(\sqrt{\dfrac{n}{k}}\right)\\ &=O\left(\int_{0}^{\sqrt{n}}\left(\sqrt{x}+\sqrt{\dfrac{n}{x}}\right)\text{d}x\right)\\ &=O(n^{3/4}) \end{aligned} \]

这个复杂度可以进一步优化,假设我们优化一部分 \(F(k)\),其中 \(k=1,2,\cdots,m\),\(m\ge\sqrt{n}\),设预处理复杂度为 \(T_0(m)\),此时 \(T(n)\) 变为:

\[\begin{aligned} T(n) &=T_0(m)+\sum_{k\in R(n);k\gt m}T(k)\\ &=T_0(m)+\sum_{k=1}^{\lfloor n/m\rfloor}O\left(\sqrt{\dfrac{n}{k}}\right)\\ &=O\left(T_0(m)+\int_{0}^{n/m}\sqrt{\dfrac{n}{x}}\text{d}x\right)\\ &=O\left(T_0(m)+\dfrac{n}{\sqrt{m}}\right) \end{aligned} \]

若 \(T_0(m)=O(m)\),由均值不等式,取 \(m=\Theta(n^{2/3})\),此时杜教筛的复杂度取最小值 \(O(n^{2/3})\)。

以上证明来自 OI-wiki

从以上证明,我们也看到:杜教筛天然适合搭配数论分块,因为两者所需前缀和的位置恰好吻合,总的复杂度仍为 \(O(n^{2/3})\),瓶颈是杜教筛。

Powerful Number 筛

我也不知道是谁引进的,但仍然怀疑来自 PE。

Powerful Number(以下简称 PN),可以用来求积性函数的前缀和,构造要求较高,但时间复杂度最低可达到 \(O(\sqrt{n})\)。

Powerful Number

首先,我们引入 PN。

定义:由唯一分解定理,记 \(n=\prod p_{i}^{\alpha_{i}}\),\(n\) 是 PN 当且仅当对所有 \(i\),满足 \(\alpha_i>1\)。特别地,令 \(1\) 也属于 \(\mathbf{PN}\)。

引理 \(4\):任何 PN 可以表为 \(a^2b^3\)。

结论显然,不证。

引理 \(5\):\(n\) 以内的 PN 有 \(O(\sqrt{n})\) 个。

证:枚举 \(a\),考虑满足条件的 \(b\) 的个数,用积分估算:

\[\int_{1}^{\sqrt{n}} \sqrt[3]{\frac{n}{x^2}} \mathrm{d}x = O\left(\sqrt{n}\right) \]

因此,预处理 \(\sqrt{n}\) 以内质数后,可以 DFS \(O(\sqrt{n})\) 枚举 \(n\) 以内 PN。

筛法过程

首先,构造积性函数 \(g\),满足 \(g(p)=f(p)\),最好能 \(O(1)\) 求和。

其次,构造积性函数 \(h\),满足 \(g\ast h=f\)。

对于质数 \(p\),\(f(p)=g(1)h(p)+g(p)h(1)=h(p)+g(p)\Rightarrow h(p)=0\)。由 \(h(n)\) 是积性函数,得 \(h(n)\) 仅在 PN 处取有效值。

由 \(f=g\ast h\),有

\[\begin{aligned} F(n) &=\sum_{i=1}^{n}(g\ast h)(i)\\ &=\sum_{d=1}^{n}h(d)G\left(\left\lfloor\dfrac{n}{d}\right\rfloor\right)\\ &=\sum_{d\in\mathbf{PN}}^{n}h(d)G\left(\left\lfloor\dfrac{n}{d}\right\rfloor\right) \end{aligned} \]

变换中利用了杜教筛的结论。

提前 \(O(\sqrt{n}\log n)\) 筛出需要的质数和 \(h(p^e)\) 的值,注意这个上界不紧,并且根据具体问题可能有优化空间。

接下来 \(O(\sqrt{n})\) 枚举 PN 求和即可。

若构造的 \(g(n)\) 需使用杜教筛求和,时间复杂度退化为 \(O(n^{2/3})\)。

标签:lfloor,right,筛法,dfrac,rfloor,sqrt,left
From: https://www.cnblogs.com/weily09/p/18318925

相关文章

  • 筛法
    筛法杜教筛,\(\textsf{Min-25}\)筛越来越边缘了啊,主要是板子,题很少建议阅读数论函数基础,数论分块,莫比乌斯函数,欧拉函数章节,了解基本概念与先要知识此处获取本节调试数据/代码包全文绝大多数内容是对[0]中讲述的粗略抄写和胡乱加工1.杜教筛在[0]中被跳......
  • 筛法学习笔记
    0.更新upd2023.5.21更新了关于powerfulnumber数量的证明upd2023.5.25更新了关于杜教筛的时间复杂度证明正文1.筛质数筛法其实就是判断质数的一个算法,但是是解决\([1,n]\)这一段区间的算法筛质数是最简单的一个用法1.1暴力最简单的方式就是对于每一个数去判断......
  • Min25 筛法
    之前学习的的确是太浅薄了,于是重新学习一下。可以做什么?对于满足条件的积性函数\(f(n)\),求其前\(n\)项和\(\sum_{i=1}^nf(i)\)。需要准备些什么?设\(N=\{x|x=\lfloor\frac{n}{i}\rfloor\}\),即为所有整除的不同点值。设\(B=\sqrtn,p_{k}\)代表\(\leB\)的所有质......
  • 取素数优化——埃拉托斯特尼筛法(Sieve of Eratosthenes)
    埃拉托斯特尼筛法(SieveofEratosthenes)是一种用来生成一定范围内所有素数的算法。其基本思想是从小到大遍历每个数,如果当前数是素数,则将其所有的倍数标记为非素数。这个过程中,所有未被标记为非素数的数即为素数。下面是使用埃拉托斯特尼筛法来计算区间[x,y]内的素数个数的修......
  • 算法课程笔记——素数朴素判定&埃氏筛法
    算法课程笔记——素数朴素判定&埃氏筛法sqrt返回浮点数,而且这样可防溢出优化i*i会更快......
  • 筛法学习笔记
    埃氏筛枚举质数\(p_i\),每次去除所有是\(p_i\)倍数的数,总效率大概是\(O(n\log\logn)\)。int_prm=0,prm[M];boolisprm[M];voidGet_phi(intn){ for(inti=2;i<=n;i++){ if(isprm[i])continue; prm[++_prm]=i; for(intj=i*i;j<=n;j+=i)isprm[j]=1; }}值......
  • 《算法笔记》系列----质数的判断(埃氏筛法)
    目录一、朴素算法二、埃氏筛法1、与朴素算法对比2、算法介绍   3、例题即代码实现一、朴素算法 从素数的定义中可以知道,一个整数n要被判断为素数,需要判断n是否能被2.3.n-1中的一个整除。只2,3..n-1都不能整除n,n才能判定为素数,而只要有一个能整除n的数出现,n就......
  • C++实现欧拉筛法
    Euler筛法介绍以筛出100以内(含100)的所有素数为例来说明一下欧拉筛法的原理。和Eratosthenes筛法一样,Euler筛法也从2开始筛,但Eratosthenes筛法会把2的倍数一批全部筛掉,而Euler筛法用2筛时仅仅把2*2(即4)筛掉,而把其它偶数留到后面再筛掉,从而避免了一个偶数被多次筛除带来的性能开销,......
  • 浅谈筛法
    浅谈筛法Euler筛Eratosthenes筛可以证明(不是“不难证明”),Eratosthenes筛的复杂度为\(\Theta(n\log\logn)\)。Eratosthenes筛的复杂度证明Dirichlet前缀和以P5495【模板】Dirichlet前缀和为例。给定\(a_1,a_2,\cdots,a_n\),求\(b_1,b_2,\cdots,b_n\),满足\[b_i......
  • 筛法思想的题目
    这道题目比较经典,或者说这种思想比较经典。这种筛法的思想。我们正着想对于每一个\(n、n-1、n-2、...、2、1\)都分解一遍质因数显然是来不及的时间复杂度达到\(O(n\sqrt{n})\)我们考虑对于每一个1e6以内的质因数的个数跑了一下程序是\(78498\)个素数定理告诉我们不超过x......