万里之行,始于足下。本博客总结近期学习到的部分数论模板,以便于日后查询使用。作者水平有限,难免存在疏漏不足,恳请诸位看官斧正。倘若我的文章可以帮到你,十分荣幸。当然,以后随着我的阅历知识增长还敬请期待后序更新。本次内容的灵感来自于lyh学长和lyk学长的讲课和近期的牛客竞赛补题。
目录
2.费马小定理(Fermat's little theorem)
更新日志:
1.于2022.4.16修正了朴素筛法的代码部分。
1.素数筛
(1)何谓素数?
总结方法前我们不妨先回溯一下知识点:什么是素数?素数又名质数,百度百科对它的定义是:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
注意:0和1不是素数!
这里有素数的两个定理,在后面的解题中比较重要:
唯一分解定理:任何一个大于1的自然数n,那么n可以唯一分解成有限个不相同的质数的乘积n = p1^a1 * p2^a2 * ... * pn^an(an为正整数)。
素数分布规律:素数的分布是越往后越稀疏的,一般来说区间内的素数个数是小于等于区间长度的1/3的。
一个数是不是素数是好判断的。但是计算机的问题规模往往会成千上万。怎么高效地找出一个范围内的素数?接下来会介绍三种筛法,如沙中淘金,找出一个范围内的素数。
(2)朴素筛法
高情商:朴素 低情商:暴力
一个一个枚举区间里的数,利用素数的定义判断枚举到的数是不是素数。
bool isprime(int x){
if(x<2) return false;//0和1不是素数也不是合数哦!
for(int i=2;i*i<=x;i++){
if(x%i==0) return false;
}
return true;
}
嗯哼?为什么是i * i <= x而不是i <= x?因为一个数的最大因子是不会超过它的二次根号的。这是一个小小的细节优化。
(3)埃氏筛(sieve of Eratosthenes)
bool numlist[maxn];//标记这个数是不是素数;maxn根据要求的范围确定
vector<int> prime;//vector容器存放求到的素数
void Eratosthenes(int n)
{
memset(numlist,true,sizeof (numlist));
numlist[0]=numlist[1]= false;//0和1不是素数哦
for(int i=2;i<=n;i++)
{
if(numlist[i])
{
prime.push_back(i);//放入vector容器
for(int j=i;i*j<=n;j++)
{
numlist[i*j]= false;//素数的倍数不是素数
}
}
}
}