质数(Primenumber),又称素数,指在大于的自然数中,除了和该数自身外,无法被其他自然数整除的数(也可定义为只有与该数本身两个正因数的数)。
质数的定义(质数的定义和性质)
如何快速判断某个数是否为质数?如何再给定区间内筛出所有的质数?
以上两个问题是大厂面试官常常喜欢考察的。本文采用多种思路,对以上两个问题进行简析。
本文所有的函数参数均默认为自然数。
文章所涉及的代码使用C++语言,使用的缺省源如下:
判断一个非负整数是否为质数。
根据定义,可以写出如下代码:
时间复杂度,空间复杂度,内约可以解决的问题。
考虑优化。
我们观察一下一个合数会被哪个数筛掉。可列出下表(记为表1):
筛掉的数42628293102122142153162182202213222242255262
(左侧为,右侧为筛掉的数。)
核心代码:
staticinlineconstuintmpf(constuintc){
for(registeruinti(2);i 发现筛掉的数都较小,我们想到,如果在一个比较小的范围内没有的约数,那是否可以判断是质数呢? 于是,我们考虑找到一个合数的最小非因数的上限。 设为一个合数,令为的最小非因数,令,显然。 由于为合数,故,故,而为的最小非因数,故。 故,。 所以,若为合数,则必定有一个不大于的因数;若中没有的因数,则为质数(除外)。 所以枚举到即可。 时间复杂度,空间复杂度,内约可以解决内的问题。 筛出中的质数,得到一张的质数表。 可以通过上面1.2中的代码判断每个数是否是质数。 时间复杂度,空间复杂度,由于大部分数为合数,常数较小,内约可以解决的问题。 观察表1,我们发现,筛掉合数的数总是质数。 于是我们猜想:一个合数的最小非因数为质数。 证明:若的不为的最小因数为且并非质数,则必然有约数满足;此时必然有,又,故且,与是的最小非因数矛盾。得证。 于是可以优化一下isprime函数。 时间复杂度(由素数定理中素数密度约为),空间复杂度,内约可以解决的问题。 图中的曲线分别表示质数数量pi(n)(蓝)、n/lnn(绿)与Li(n)(红)。 既然可以用质数判断一个数是否为合数,那为什么不直接用质数筛出合数呢?这样可以减少很多不必要的计算吧。 容易想到,我们从开始枚举,用isp[i]表示之前有没有被筛过,若枚举到一个数未被筛过,则其为质数,用之筛去后面的合数。 时间复杂度,空间复杂度,内约可以解决的问题。 这种方法被称为埃氏筛(埃拉托斯特尼筛法,sieveofEratosthenes),是一种非常经典的入门筛法。 时间复杂度直观证明: 假设素数在区间内按照质数定理的结论均匀分布,将求和转化为积分,可得计算次数约为 2.3的主要缺点是合数被筛出多次,造成时间复杂度偏大。 考虑使每个合数被其最小质因数筛掉。 设大于的正整数的最小质因数为(若为质数显然有),定义。 考虑枚举素数和大于的正整数,使得(此时显然)。 那么,如果我们能找到一种方法枚举出所有这样的数对,我们就可以筛出所有合数(即)。 比较显然地,有,故等价于。 于是,我们枚举满足为质数且的数对即可。 考虑枚举,发现确定后不太容易枚举。 于是考虑枚举大于的正整数,确定后枚举质数,使得。 我们确定后从小到大枚举质数,则第一个满足的质数即为,此前枚举到的均满足。 于是可以写出如下代码: 时空复杂度,内约可以解决的问题。 这种方法被称为线性筛法(欧拉筛法,sieveofEuler),是一种非常常用的筛法。 由于这种方法可以方便地区分筛掉合数的两个数之间是否存在倍数关系,故常常可用来筛积性函数。 好了,关于质数的一系列面试问题我们就聊到这里,记得点赞哦~~
发表评论