【算法介绍】素数筛法
算法简介:骑士团长的一日假期
素数筛法是作为数论基础而出现的。数论本质上是对数字的一些性质的研究,而质数是这里很重要的一部分。不同的质数筛有着不同的特性,用来解决不同的题目,接下来我们会大概了解几种。
算法演示:若困你于无风之地
$O(\sqrt n)$ 判断单个质数
这个很简单,由于考虑到因子是一对一对的,所以一对因数中较小的那一个最多最多是 $\sqrt n$ ,也就是说我们只需要枚举 $2$ ~ $\sqrt n$ 复杂度 $O(\sqrt n)$ ,不枚举 $1$ 是因为所有数字都是 1 的倍数,素数也只是被 $1$ 和它本身整除。
1 | bool isPrime(int x) { |
$O(n\log\log n)$ 多个快筛
这个就属于新开一篇了,我们大概的思考一下,什么东西不是质数,如果已知不等于一的数字,他除了自己以外的倍数一定都是合数,这样的话可以写出如下代码:
1 | int flag[N]; // 是不是质数(是 - 0, 否 - 1) |
这样的话我们会发现有大量的复杂度浪费,一个数字,它会被他所有的因子(注意是所有的,不论质数还是合数)筛一遍,这是不优秀的。所以我们可以让质数去筛合数,这样就没有问题了。
1 | int flag[N]; // 是不是质数(是 - 0, 否 - 1) |
而复杂度的话,我们发现每次只用素数去筛,复杂度形如:$O(\cfrac n 2 + \cfrac n 3 + \cfrac n 5 + \dots)$ 而由于 $n$ 以内质数的量级大约是 $\log n$(准确点是 $\ln n$ )然后又是类调和级数,估计复杂度为 $O(n\log\log n)$ 。
以上的筛法被称为埃氏筛。
$O(n)$ 线性筛
我们发现一个这里复杂度的瓶颈在于一个数会被他所有的素因子筛到,这个是不允许的,我们思考如何可以让一个数字只被他最小的素因子筛到。我们记录一个素数表存储已经记录好的素数。对于一个素数,他的倍数一定是合数,这样的话就只需要对于每次筛出来的素数和一个数相乘就可以了。
1 | bool isprime[MAXN]; // isprime[i]表示i是不是素数 |
这种筛法每个数都只会被筛一次,被当作一次筛子,复杂度 $O(n)$ ,被称为欧拉筛。
算法实战:在此世的星辰外
查看相关标签喵!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 祝馀宫!
评论