数论
数论
一、整除
(1)整除:
整除概念: 若
if(a%b==0) cout<<"b|a";
例1: 输入一个正整数n判断n是否能够被3和5整除,如果能够输出“Yes” ,否则输出“No”。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
if(n%3==0&&n%5==0)
cout<<"Yes";
else
cout<<"No";
return 0;
}
(2)余数:
余数概念: 若
r = a % b;
余数的计算方法为
r = a - a / b * b;
二、同余
同余概念: 若a,b为两个整数,且它们的差
同余性质:
1、对称性:若
2、传递性:若
3、同加性:若
4、同乘性:若
5、同幂性:若
6、相关推论:
①若
②若
③若
④若
⑤除法不满足同除性,具体详见 逆元
⑥若
例2: 求
算法思路: 直接求
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,p,s=1;
cin>>a>>b>>p;
a=a%p;
//利用a^b%p = (a%p)^b%p减少运算规模,这句不写也不会错,注意数据溢出。
for(int i=1;i<=b;i++)
s=s*a%p;
//利用s*a%p=(s%p*a%p)%p,这里因为s和a都小于p故 (s%p*a%p)%p=s*a%p
cout<<s;
return 0;
}
当然还有更高效的快速幂算法,自行百度,本资料不做介绍。
三、因数
(1)因数:
因数概念: 如果a÷b余数为0,就称b为a的因数或约数。比如说正整数 36 的因数是:
例3: 输入一个正整数n,求出n的所有的因数,从小到大并以空格隔开。
算法思路: 枚举1到n之间(包括1和n)所有数,将能整除n的数输出。算法时间复杂度为
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
if(n%i==0)
cout<<i<<" ";
return 0;
}
算法评估: 因数都是成对出现的, 比如说36的因数
算法思路: 只需要枚举1到
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
//不使用i<=sqrt(n)这样每循环一次都会计算一次根号n,导致效率降低
for(int i=1;i*i<=n;i++)
if(n%i==0){
cout<<i<<" ";
if(i!=n/i) cout<<n/i<<" ";
//⭐判断判断两个因数是否相同,不同则输出
}
return 0;
}
(2)公因数:
公因数概念: 对于两个正整数a和b,如果c能够同时整除a和b ,则c是a和b的公因数。
例4: 输入两个正整数a和b,求出a和b的所有公因数,从小到大并以空格隔开。
算法思路: 枚举1到 min(a , b),如果能同时整除 a和b的数就是a和b的公因数。 min(a,b)表示 a和b的最小值。算法时间复杂度为
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
cin>>a>>b;
int n=min(a,b); //n为a和b的最小值
for(int i=1;i<=n;i++) //i<=a&&i<=b
if(a%i==0 && b%i==0)
cout<<i<<" ";
return 0;
}
(3)最大公因数 GCD (Greatest Common Divisor):
最大公因数概念: a和b的所有公因数中最大的数。又叫做最大公约数。
例5: 输入两个正整数a和b,求出a和b的最大公因数。
算法思路: 从大到小枚举
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
cin>>a>>b;
int m=min(a,b); //n为a和b的最小值
for(int i=m;i>=1;i--) //从大到小枚举,直到遇到第一个能够整除a和b的数
if(a%i==0&&b%i==0){
cout<<i;
break; //这里也可以使用return 0;
}
return 0;
}
算法评估: 对于上面这个程序,他的运行效率低,如果a和b很大的话,程序执行时间会随着a和b增加而成线性增加。故引入欧几里得算法(辗转相除法)。
算法思路:
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,r;
cin>>a>>b;
do{
r=a%b;
a=b;
b=r;
}while(r);
cout<<a;
return 0;
}
递归实现:
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
return b==0 ? a : gcd(b,a%b);
}
int main(){
int a,b;
cin>>a>>b;
cout<<gcd(a,b);
return 0;
}
(4)最小公倍数LCM (Lowest Common Multiple):
最小公倍数概念: a和b的所有公共倍数中最小的数。
例6: 输入两个正整数a和b,求出a和b的最小公因数。
算法思路:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
//⭐由于a和b的乘积会超过int,用long long处理数据溢出
ll a,b,r;
cin>>a>>b;
//记录a和b的乘积,因为a和b的值会在辗转相除的过程中改变
ll times=a*b;
do{
r=a%b;
a=b;
b=r;
}while(r);
//a为最大公因数,用公式lcm(a,b)=a*b/gcd(a,b)求出最小公倍数
ll lcm=times/a;
cout<<lcm;
return 0;
}
(5)互质:
互质概念: 有正整数a和b,若a和b的最大公因数为1,则称为a和b互质。
例7: 输入两个正整数a和b,求判断a和b是否互质,如果互质输出“Coprime”,否则输出“Not Coprime”。
算法思路: 利用欧几里得算法计算出a和b的最大公因数,判断若为1,则a和b互质。算法时间复杂度为
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,r;
cin>>a>>b;
do{
r=a%b;
a=b;
b=r;
}while(r);
if(a==1) cout<<"Coprime";
else cout<<"Not Coprime";
return 0;
}
(6)裴蜀定理:
裴蜀定理概念: 若a,b是整数,且记
证明方法:
设
设
可见r也是
又因为s是
又因为
(7)扩展欧几里得算法 *
扩展欧几里得算法概念: 该算法是用来求已知两个正整数
算法思路:
化简
对于
可得
根据
可得
#include<bits/stdc++.h>
using namespace std;
int exgcd(int a,int b,int &x,int &y){
int gcd,tmp;
if(b==0){
x=1;
y=0;
return a;
}
gcd=exgcd(b,a%b,x,y);
tmp=x;
x=y;
y=tmp-a/b*y;
return gcd;
}
int main(){
int a,b,x,y,gcd;
cin>>a>>b;
gcd=exgcd(a,b,x,y);
cout<<x<<" "<<y<<endl;
cout<<gcd;
return 0;
}
四、质数
(1)判断质数
质数概念: ①在大于1的数当中 。②因数只有1和它本身 即为素数也称作质数。
例8: 输入正整数n,判断n是否是质数,如果是质数输出“Prime”,否则输出“Not Prime”。
算法思路: ①判断n是否小于2。 ②枚举2到
#include<bits/stdc++.h>
using namespace std;
/*自定义函数isPrime用于实现判断素数的功能,如果是素数函数返回1,不是则返回0*/
int isPrime(int n){
if(n<2) return 0; //如果n小于2 函数返回0
for(int i=2;i*i<=n;i++) //从2开始枚举到
if(n%i==0)
return 0; //如果n存在第三个因数 函数返回0
return 1; //如果n大于1并且不存在除了1和本身以外的第三个因数 函数返回1
}
int main(){
int n;
cin>>n;
if( isPrime(n) ) //将n的值代入函数isPrime,用于判断n是否是素数
cout<<"Prime";
else
cout<<"Not Prime";
return 0;
}
(2)筛选范围内的质数
例9: 输入正整数n,输出1到n之间所有的质数,用空格隔开。
(a)朴素筛选法
算法思路: 枚举2至n之间的所有数,用判断质数的函数依次判断。算法时间复杂度
#include<bits/stdc++.h>
using namespace std;
int isPrime(int n){
if(n<2) return 0; //如果n小于2 函数返回0
for(int i=2;i*i<=n;i++) //从2开始枚举到根号n
if(n%i==0)
return 0; //如果n存在第三个因数 函数返回0
return 1; //n大于1 且 因数只有1和本身 函数返回1
}
int main(){
int n;
cin>>n;
for(int i=2;i<=n;i++)
if(isPrime(i)) //判断i是否是质数
cout<<i<<" ";
return 0;
}
(b)埃式筛选法
算法思路: 枚举2到n之间的所有数,每次枚举时去掉质数的倍数,算法时间复杂度
#include<bits/stdc++.h>
using namespace std;
int isPrime[1000]; //数组isPrime[i]记录i是否为质数,是为0,不是为1
int priList[1000]; //数组priList[k]记录2到n之间第k个质数
int main(){
int n,k=0;
cin>>n;
for(int i=2;i<=n;i++)
if(isPrime[i]==0){ //判断是否为质数
priList[++k]=i; //将质数i存入数组当中
for(int j=2*i;j<=n;j+=i)
isPrime[j]=1; //将质数i的倍数标记为非质数
}
for(int i=1;i<=k;i++)
cout<<priList[i]<<" ";
return 0;
}
(c)欧拉筛选法(线性筛选法)
算法思路: 对于埃式筛选法,影响效率的最大问题就是重复,比如合数24分别被2、3、4、6处理过,存在重复处理,为了提高效率只需要处理一次即可。故如果该数是合数的话,只需要让该数被它的最小的质因数处理即可,例如合数24只需要被2处理,同理30只需要被2处理,而不再需要被5处理。虽然看代码上不是一个
#include<bits/stdc++.h>
using namespace std;
int isPrime[1000]; //数组isPrime[i]记录i是否为质数,是为0,不是为1
int priList[1000]; //数组priList[k]记录2到n之间第k个质数
int main(){
int n,k=0;
cin>>n;
for(int i=2;i<=n;i++){
if(isPrime[i]==0) //将素数存入质数表
priList[++k]=i;
//枚举素数表中的数,将i的priList[j]倍数标记为非质数
for(int j=1;j<=k && i*priList[j]<=n;j++){
isPrime[ i*priList[j] ]=1;
//如果当前这个数i的因数包含质数priList[j]即停止筛选。
if(i%priList[j]==0)
break;
}
}
for(int i=1;i<=k;i++)
cout<<priList[i]<<" ";
return 0;
}
(3)质数相关定理
1、唯一分解定理
若整数
质因数概念: 能整除给定正整数的质数。
例10: 输入正整数n,输出n的所有的质因数并以空格隔开。
算法思路: 先从最小的质数2开始分解,直到不能能分解的时候选择下一个质数3,以此类推,直到将该数n分解为1为止。若n无法被分解,说明n现在已经是素数。例如: |
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
//枚举2到根号n之间的数,如果能整除即为质因数
for(int i=2;i*i<=n;i++)
while(n%i==0){
cout<<i<<" ";
n/=i;
}
//如果最终n不为1,则说明当前n为质数。
if(n!=1)
cout<<n;
return 0;
}
2、威尔逊定理
若p为质数,则
同理,若某正整数
利用
3、费马定理
若
证明方法:
①
② 因此对于这
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】