Part 0:概念
先给几个概念(很重要):
- 合数:如果$xy=z\text{且}x,y\text{为正整数}$,我们就说$x,y\text{是}z\text{的合数}$
- 素数:如果数$a$的合数只有$1,a$,则$a$就是一个素数
- 整除:整数$b$除以非零整数$a$,商为整数,且余数为零, 我们就说$b$能被$a$整除,记做$a | b$。数学中,求一个数的余数的运算叫做取余,用$a MOD b$表示求a除以b的余数,计算机中用
%
当然,如果有$a | b$,那么我们可以写成$a MOD b = 0$ - 不含0,1的所有自然数除了素数就是合数
Part 1:普通筛法及优化到根号
首先,从普通筛法开始:
1 |
|
判断一个数是不是素数,就从他的定义入手
定义是啥?回顾一下:
如果数$a$的合数只有$1,a$,则$a$就是一个素数
再次回顾合数的概念:
如果$xy=z\text{且}x,y\text{为正整数}$,我们就说$x,y\text{是}z\text{的合数}$
我们知道,不含0的所有自然数不是素数就是合数,而我们算法的名字叫做筛素数,而素数的定义离不开合数
干脆,我们把合数筛掉吧!
我们从$xy=z入手$。显然,$x,y,z \ neq 0$,可以大胆的除一下:$x = \frac{z}{y}$
如果你比较熟悉开头的那些定义,你就会发现:因为$x$是个正整数,所以$z | y$,也就是$z MOD y = 0$
这个地方可以好好理解一下
那么,我们开始填充代码:
1 |
|
好的,代码是出来了
可以更快吗?当然可以
我举个例子:36。
36的因数有:{1,36},{2,18},{3,12},{4,9},{6,6}
把36的因数排个序:{1,2,3,4,6},{6,9,12,18,36}
以6为分界线,我们可以把它分为了两份,请注意:
$\sqrt{36} = 6$
哇塞!我们只需要循环到$\sqrt{n}$我们就可以结束了(因为因数是两两对应的(如果不考虑去重(比如49的因数表暂时定为:1,7,7,49)))
改进一下:
1 |
|
Part 2 真正的筛——埃式筛
刚才那个算法的思想再怎么说,也是个取素数
但是,判断合数显然比判断素数更简单
那么,为什么不把合数筛走,剩下的不就是素数了吗?
好办法!接下来我们想下怎么筛
其实啊,可以发现某一个不为0/1的数 × 另一个不为0/1的数 = 合数
那么,假设有一个数$q$,我们只需要筛掉$1q,2q,3q\text{……一直到}iq>n$时,接着q++,继续筛!
上代码:
1 |
|
哦太棒了!
但是!请你自己模拟一下这个过程,你会发现个奇怪的过程拖慢了速度!
当i = 2 => 2i=4,3i=6,4i=8…
当i = 3 => 2i=6,3i=9,4i=12…
当i = 4 => 2i=8…
Oh No!又开始重复了!
重复怎么办?再筛掉!
Part 3:线性筛
1 | //代码来自https://www.cnblogs.com/Return-blog/p/12307038.html |
Oh太棒了!你终于搞到了最快的方法
最后送你个福利:证明一下为什么:当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉
首先,prime数组中的素数是递增的(这个不用说)
因为i中含有prime[j],prime[j]比prime[j+1]小
即$i=k\times prime[j]$
那么$i\times prime[j+1]=(k\times prime[j])\times prime[j+1]$
也就是$k’\times prime[j]$
看不懂没关系,板子背下来就好了~