初等数论c++.docx
《初等数论c++.docx》由会员分享,可在线阅读,更多相关《初等数论c++.docx(15页珍藏版)》请在冰豆网上搜索。
初等数论c++
备注:
纯手写代码,注释。
数论
1、素数
(1)暴力求解法
ﻩ根据素数得概念,没有1与其本身没有其她正因数得数。
所以只需枚举比这个数小得数,瞧能整除即可;
C++代码:
#include<iostream〉
#include〈cstdio〉
#include<cmath>
using namespace std;
booldetermine(intnumber)
{
if(n<=2)returnfalse;
if(!
n%2)returnfalse;
for(inti=3;i<=ceil(sqrt(number));i+=2)
//去掉了偶数得判断,效率提高一倍
ﻩ/*如果number整除以i,那么会得到两个得因数,
而较小得那个因数不会超过number得二分之一次方;
所以只需判断到number得平方根向上取整即可;*/
ﻩﻩif(number%i);
elsereturnfalse;
ﻩreturntrue;
}
intmain()
{
ﻩintsum;
cin>〉sum;
if(determine(sum))
ﻩcout<〈"YES!
”;
ﻩelsecout<<"NO!
”;
ﻩreturn0;
}
时间复杂度:
o(sqrt(n)/2);
空间复杂度:
几乎没有;
(2)一般线性筛法:
ﻩ因为任何一个合数都能分解成几个素数相乘得形式;
所以可以做一个表,首先把2设为质数,然后将2得倍数设为合数,剩下得数就就是新得到得质数,然后重复这个过程,直到筛到合适得范围即可;
但就是这个算法有缺陷:
1、同一个数可能被筛多次,这就产生了多余得步骤。
2、占用空间很大,如果使用bool数组得话,只能筛到1e9;
3、从1—n筛,不能从m-n开始筛;
C++代码:
#include<cstring〉
#include#include<iostream〉
usingnamespace std;
bools[1000000000];
intm,n;
intmain()
{
ﻩcin>>m>>n;
memset(s,true,n);
ﻩs[0]=s[1]=0;
ﻩ//输出M—N之间所有素数;
ﻩfor(int i=2;i<=ceil(sqrt(n));++i)
ﻩif(s[i])
{
ﻩﻩﻩfor(int j=i;j〈=n;++j)
ﻩﻩif(s[i*j])
ﻩﻩs[i*j]=false;
ﻩ}
for(int i=m;i〈=n;++i)
ﻩif(s[i])
cout<
return0;
}
时间复杂度:
o(n*loglogn);
空间复杂度:
很大!
注意数据大得话可能会爆空间;
(3)线性筛法求素数
这个占空间就更大了,需要使用一个bool数组与int数组
而亲身试验得到int数组最多开到1e8……
很无语,快确实就是快了,但就是测试数据一大,爆空间就更容易了;
#include〈iostream>
#include〈cstdio〉
#include〈cmath>
usingnamespace std;
intm,n,sum;
bool inp[1000000000];
ints[100000000]={0,0};
intmain()
{
ﻩcin>〉m>〉n;
ﻩfor(inti=2;i<=n;++i)
ﻩ{
ﻩif(!
inp[i])
ﻩs[sum++]=i;
for(intj=0;jﻩ{
ﻩinp[i*s[j]]=true;
ﻩif(!
(i*s[j]))
ﻩﻩbreak;
ﻩ}
ﻩ}
ﻩfor(inti=m;i<=n;++i)
ﻩif(!
inp[i])
cout<
return0;
}
2、唯一分解定理
任何数都可以被唯一得分解成多个素数之积
例如:
456=2*2*2*3*19;
C++代码:
#include
#include#include#include〈algorithm>
#include〈cstdlib>
using namespacestd;
bool s[1000000];
int m,n,sum=0,num;
int Prime[1212121];
intzhi[1500];
voidPrimes()
{
ﻩfor(inti=1;i〈=num;++i)
ﻩs[i]=true;
ﻩs[0]=s[1]=0;
for(inti=2;i<=num;++i)
ﻩﻩif(s[i])
ﻩ{
Prime[++sum]=i;
ﻩﻩﻩfor(intj=i;j<=num;++j)
ﻩif(s[i*j])
ﻩﻩﻩs[i*j]=false;
ﻩ}
}
intmain()
{
ﻩintflag=0;
ﻩcin>〉num;
intnumber=num;
Primes();
if(s[num])
ﻩ{
cout<〈num<〈'='〈〈num;
ﻩﻩreturn0;
ﻩ}
cout〈<num<<"=”;str.chu();
while(num>1)
ﻩfor(inti=1;num〉1&&i<=sum;++i)
ﻩif(!
(num%Prime[i]))
ﻩ{
ﻩﻩzhi[++flag]=Prime[i];
ﻩnum/=Prime[i];
ﻩﻩ}
sort(zhi+1,zhi+flag+1);
cout〈<zhi[1];
ﻩfor(inti=2;i〈=flag;++i)
cout<〈"*"<〈zhi[i];
return0;
}
首先做一个质数表,并把质数存到数组里,然后用数模每个素数,如果为0则记录素数,最后排个序输出;
4、欧拉函数
欧拉函数φ(n)为不大于n得与n互素得数得个数;
A与B互素,表示a与b得最大公约数为1,即(a,b)=1;
欧拉函数得符号φ读作fhi,在搜狗得特殊符号里可以找到;
其中pi为x得质因数,其中φ
(1)=1(唯一与1互质得数就是1本身)
设n为正整数,以φ(n)表示不超过n且与n互素得正整数得个数,称为n得欧拉函数值
φ:
N→N,n→φ(n)称为欧拉函数。
几个性质(来自XX百科)
1、若n就是质数p得k次幂, ,因为除了p得倍数外,其她数都跟n互质。
2、欧拉函数就是积性函数--若m,n互质,
3、特殊性质:
当n为奇数时, ,证明与上述类似。
4、若n为质数则
5、设p就是素数,a就是一个正整数,那么
C++实现:
#include<cstring〉
#include<cmath〉
#include<iostream>
#include<algorithm〉
#include〈cstdlib〉
usingnamespacestd;
bools[1000000];
intm,n,sum=0,num;
int Prime[1212121];
int zhi[1500];
boolasd[1500];
intphi(intn)
{
int i,rea=n;
for(i=2;i*i<=n;i++)
{
if(n%i==0)
{
rea=rea-rea/i;
while(n%i==0) n/=i;
}
}
if(n〉1)
rea=rea-rea/n;
returnrea;
}
voidPrimes()
{
for(inti=1;i<=num;++i)
ﻩﻩs[i]=true;
ﻩs[0]=s[1]=0;
ﻩfor(inti=2;i<=num;++i)
ﻩif(s[i])
ﻩ{
Prime[++sum]=i;
ﻩﻩfor(intj=i;j〈=num;++j)
if(s[i*j])
ﻩﻩﻩs[i*j]=false;
ﻩ}
}
int main()
{
intflag=0;
cin>>num;
ﻩintnumber=num;
Primes();
if(num==1||!
num)
ﻩ{
ﻩcout〈〈"fhi"〈<’(’〈ﻩﻩcout〈〈num;
ﻩﻩreturn0;
}
ﻩif(s[num])
{
cout<〈"fhi”〈<'(’<ﻩreturn 0;
ﻩ}
while(num>1)
ﻩfor(inti=1;num〉1&&i〈=sum;++i)
ﻩif(!
(num%Prime[i]))
{
ﻩﻩﻩzhi[++flag]=Prime[i];
ﻩnum/=Prime[i];
}
ﻩintfenzi=1,fenmu=1;
sort(zhi+1,zhi+flag+1);
ﻩfor(inti=1;i〈=flag;++i)
if(!
asd[zhi[i]])
ﻩﻩ{
ﻩasd[zhi[i]]=true;
fenzi*=zhi[i]-1;
ﻩfenmu*=zhi[i];
ﻩ}
cout<〈"fhi(”<//cout<〈"fhi("</*这就是另一种求欧拉函数值得方法*/
return0;
}
5、欧几里得算法
辗转相除法,根据公式(a,b)=(b,r)
其中r为a%b,即a/b;
C++代码:
(1)递归
#include#include<cstdio>
usingnamespacestd;
intGCD(inta,int b)
{
ﻩif(a%b)
ﻩ return GCD(b,a%b);
ﻩelse returnb;
}
intmain()
{
inta,b;
ﻩcin>>a>〉b;
cout〈〈GCD(a,b);
return0;
}
(2)递推
#include<iostream>
using namespace std;
intmain()
{
ﻩinta,b,r;
ﻩcin〉>a〉>b;
ﻩr=m%n;
while(r!
=0)
ﻩ{
a=b;
ﻩb=r;
ﻩr=m%n;
ﻩ}
ﻩcout<ﻩreturn 0;
}
6、扩展欧几里得
扩展欧几里得又称斐蜀定理,对于不完全为0得非负整数a,b,gcd(a,b)表示a,b得最大公约数,必然存在整数对x,y,使得gcd(a,b)=ax+by;
求同余方程
#includevoidexgcb(inta,int b,int&x,int&y)
{
ﻩif(!
b)
ﻩ{
ﻩx=1;y=0;
return;
}
intq=a/b;
ﻩintr=a%b;
exgcb(b,r,y,x);
y-=q*x;
}
intmain()
{
intx,y;
inta,b;
scanf(”%d %d",&a,&b);
ﻩexgcb(a,b,x,y);
while(x〈0)
ﻩx+=b;
ﻩprintf("%d",x);
return0;
}
求乘法逆元
#include〈cstdio>
void exgcb(int a,int b,int&x,int &y)
{
if(!
b)
{
ﻩx=1;y=0;
ﻩreturn;
}
intq=a/b;
int r=a%b;
exgcb(b,r,y,x);
y—=q*x;
}
int Multiplicativeinverse(inta,intb)
{
int x,y;
ﻩint gcb=GCD(a,b,x,y);
if(1%gcb)return-1;
x*=1%gcb;
ﻩb=abs(b);
ﻩintanswer=x%b;
while(answer<=0)
ﻩﻩanswer+=b;
return answer;
}
intmain()
{
ﻩintx,y;
ﻩinta,b;
ﻩscanf(”%d %d",&a,&b);
ﻩexgcb(a,b,x,y);
while(x<0)
ﻩx+=b;
ﻩprintf(”%d\n",x);
cout<<Multiplicative inverse(a,b);
ﻩreturn0;
}
求线性方程ax+by=c
这个方程等同于ax≡c(modb)
所以