初等数论——素数,逆元,EXGCD有关
初等数论
素数定义#
设整数
。如果 除了平凡约数以外没有其他约数,那么称 为素数(不可约数)。 若整数
且 不是素数,则称 为合数。 ——————OI Wiki
素数的判定(素性测试)#
如何判断一个数
很显然我们可以暴力的枚举
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n;
inline int pd(int x)
{
int len=sqrt(x);
for(int i=2;i<=len;i++)
if(x%i==0)return 0;
return 1;
}
signed main()
{
cin>>t;
while(t--)
{
cin>>n;
if(pd(n))cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
return 0;
}
Miller-Rabin素性测试#
这个算法就一个用法:判断一个数是不是素数。
这个算法的依据是:
费马小定理#
对于任意的质数
证明如下:
这个式子可以用欧拉定理来证明:
首先,把式子稍微变换一下得到:
那么是不是当一个数
答案是 false
所以我们需要下面的定理来减小错误的概率。
二次探测定理#
若
证明:
算法过程#
我们把
能这么分的原因是
当
然后随机选择一个数
然后如果要是当前的值就是
百度告诉我们若
那么我们用
当然也不会是
我们需要注意的是,因为是次方运算有点猛,所以推荐使用龟速乘来防止溢出。
埃氏筛#
从小到大枚举范围,如果当前数没有被标记就说明是质数,直接加进去,然后枚举在范围内的当前数的倍数标记,然后一直重复直到结束,算是比较优秀的筛了(至少相对暴力)据说复杂度
图片来自 b 站董晓算法。
线性筛#
重中之重,必须掌握——————我说的
当然我觉得这个还是更快一些的。
我们首先要知道的就是上面的筛慢在什么地方?
因为有很多数被重复筛了好几次,那么我们有什么办法能不能让一个数只被筛一次,让复杂度成为真正的线性呢?
答案是 true
我们和上面一样,只不过在筛的时候,我们不是枚举当前质数的多少倍了,而是每枚举到一个数
code:
#include<bits/stdc++.h>
using namespace std;
int n,m,ans[100010000],vis[100010000],cnt;
int main()
{
std::ios::sync_with_stdio(0);
cin>>n>>m;
for(int i=2;i<=n;i++)
{
if(!vis[i])ans[++cnt]=i;
for(int j=1;j<=cnt&&ans[j]*i<=n;j++)
{
vis[ans[j]*i]=1;
if(i%ans[j]==0)break;
}
}
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
cout<<ans[x]<<endl;
}
return 0;
}
EXGCD#
欧几里得应该在上一篇关于数论的文章里讲到了,而且也不难,所以在此不再赘述。
基本算法:对于不完全为
证明:
设
显然当
当
设
根据朴素的欧几里得原理有
则:
即:
等量代换得:
这样我们就可以一直递归求
因为总有一个时候
#include<bits/stdc++.h>
#define int long long
using namespace std;
int x,y,n,m;
inline void exgcd(int a,int b)
{
if(b==0){x=1,y=0;return ;}
exgcd(b,a%b);
int t=x;
x=y;
y=t-a/b*y;
}
signed main()
{
cin>>n>>m;
exgcd(n,m);
x=(x%m+m)%m;
cout<<x<<endl;
return 0;
}
或者这种写法:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int x,y,n,m;
inline void exgcd(int a,int b,int &x,int &y)
{
if(b==0){x=1,y=0;return ;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
signed main()
{
cin>>n>>m;
exgcd(n,m,x,y);
x=(x%m+m)%m;
cout<<x<<endl;
return 0;
}
逆元#
主要还是模运算下乘法运算的逆元,下文中逆元都会带有下标
如果一个线性同余方程
,则称 为 的逆元,记作 。
——————OI Wiki
EXGCD求逆元#
老朋友了,表示
然后看一下这个:
模意义下的除法在大多数时候都不适用。
当题目中要求对答案取模,但我们又不得不使用一般的除法的时候,就需要用逆元的乘法来代替除法。
根据逆元的定义得:
左右两边同乘
上减下得:
移项得:
两边同时加一得:
设一个正整数
两边同时加
因为
所以:
根据我们的扩展欧几里得可以得知:当知道
知道
费马小定理求逆元#
首先回到我们前面推的柿子;
好的现在我们可以知道当
稍微变换一下得:
因为
根据我们上面得出得柿子一结合就可以得出:
所以我们就可以用:
线性求逆元#
前面的都太慢了,我们要追求效率。
首先我们有一个
我们设
然后放到
乘上
移项得:
把
贴一下代码:
inv[1] = 1;
for(int i = 2; i < p; ++ i)
inv[i] = (p - p / i) * inv[p % i] % p;
积性函数#
数论函数:在数论上,对于定义域为正整数,值域为复数的函数,我们称之为数论函数。
非完全积性函数:对于数论函数
完全积性函数:对于数论函数
简单积性函数#
参考资料:OI WIKI,自为风月马前卒的博客,https://www.cnblogs.com/zylAK/p/9569668.html ,b站董晓算法。
作者: 北烛青澜
出处:https://www.cnblogs.com/Multitree/p/17410166.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)