总结下数论中的一些简单的知识点,留着以后忘的时候查看,如有不足之处希望大家指出,我会更加努力学习。好了不说废话了,直接给出各个函数的代码吧。
/**
*@xiaoran
*一直在研究数论,现在来研究一下数论中的常用算法
*数论一直是我的弱项(其实没有什么强项),吧这段时间的
*学习和以前的学习都写出来加深下印象。
*数论中最常用的算法也就是那几种。
*1、求一个数的所有因子和:FactorSum(n)
*2、求一个数的约数的个数:CountFactor(n)
*3、判断一个数是不是素数:IsPrime(n)
*4、找出[1..n]之间的所有素数:PrintPrime(n)
*5、最大公约数和最小公倍数:Gcd(a,b)和Lcm(a,b)
*6、扩展最大公约数和模线性方程(包括模逆元):ExtGcd(a,b,&d,&x,&y)和ModeQ(a,b,n)
*7、欧拉函数(即小于n且与n互素的数的个数):Phi(n)
*8、求出[1..n]的所有数的欧拉函数值:PhiTable(n)
*9、同余定理(大数取摸):
*10、幂取模:PowMod(a,n,m)
*/
各个函数代码:
//求一个数的所有因子和:FactorSum(n)
int FactorSum(int n){
int sum=0;
int k=(int) sqrt(n+0.5);
for(int i=1;i<=k;i++){
if(n%i==0) sum+=i+n/i;
}
if(k*k==n){//如果n是完全平方数,k就会被多计算一次
sum-=k;
}
return sum;
}
/**
*求一个数的约数的个数:CountFactor(n)
*唯一分解定理n=p1^a1*p2^a2*p3^a3*...pn^an
*n的约数个数为:s=(a1+1)*(a2+1)...(an+1)
*36=2^2*3^2
*s=(2+1)*(2+1)=9;
*1 2 3 4 6 9 12 18 36共9个
*/
int CountFactor(int n){//算法有问题,现给出修改后的算法
int ans=1;
int k=(int) sqrt(n+0.5);
for(int i=2;i<=k;i++){
int s=1;
while(n%i==0){
n/=i;
++s;
}
ans*=s;//此时s已经加1
}
return ans;
}
int CountFactor(int n){
int s,i,ans=1;
int k=(int) sqrt(n+0.5);
for(i=2;i<=k;i++){
s=1;
while(n%i==0){
n/=i;
++s;
}
//cout<<i<<" "<<ans<<" "<<n<<endl;
ans*=s;//此时s已经加1
if((n%i==0)&&i!=k&&isPrim(n/i)){
s=1;
while(n%(n/i)==0){
n/=(n/i);
++s;
}
ans*=s;//此时s已经加1
}
if(n==1) break;
}
if(isPrim(n)==1){
ans*=2;//此时s已经加1
}
return ans;
}
/**
*判断一个数是不是素数:IsPrime(n)
*一个数只能被1和其本身整除,这个数是素数
*否则是合数
*/
bool IsPrime(int n){//试用于不太大的整数
if(n==1) return false;//1不是素数
for(int i=2;i*i<=n;i++){
if(n%i==0) return false;
}
return true;
}
/**
*找出[1..n]之间的所有素数:PrintPrime(n)
*/
int PrintPrime(int *A,int n,int *Prime){
//找出[1..n]的所有,存在Prime中,并返回素数的个数
memset(A,0,sizeof(A));//A.length>n
int k=0;
for(int i=2;i<=n;i++){
if(!A[i]){
Prime[k++]=i;
for(int j=i*2;j<=n;j+=i){
A[j]=1;
}
}
}
//现在[1..n]之间的素数已经存到Prime中了
return k;
}
/**
*最大公约数和最小公倍数:Gcd(a,b)和Lcm(a,b)
*辗转相除求最大公约数,Lcm(a,b)=a*b/Gcd(a,b)
*/
int Gcd(int a,int b){//递归形式
if(b==0) return a;
else return Gcd(b,a%b);
}
int gcd(int a,int b){//迭代形式
int r;
while(b){
r=a%b;
a=b;
b=r;
}
return a;
}
int Lcm(int a,int b){
return a/Gcd(a,b)*b;
}
/**
*ax+by=gcd(a,b)//必然有解
*对于ax+by=c;如果c是gcd(a,b)的倍数,则ax+by=c必有整数解
*扩展最大公约数ExtGcd1(a,b,&d,&x,&y)
*扩展最大公约数ExtGcd2(a,b,&x,&y)
*/
int ExtGcd1(int a,int b,int &d,int &x,int &y){
if(b==0) {d=a; x=1; y=0;}
else {ExtGcd1(b,a%b,d,y,x); y-=x*(a/b);}
}
int ExtGcd2(int a,int b,int &x,int &y){
if(b==0) {x=1; y=0; return a;}
int d=ExtGcd2(b,a%b,x,y);
int t=x; x=y; y=t-a/b*y;
return d;
}
/**
*模线性方程:ax=b%n;对于这个方程我们称ax和b模n同余
*即ax%n=b%n;那么我们知道ax-b必然是n的整数倍,我们设ax-b是n的y倍
*有ax-b=ny ---> ax-ny=b; 我们注意到这个形式和ax+by=c类似,可以用扩展gcd就行求解
*特别的当b=1是,ax=1%n,即ax和1模n同余,此时的解x还有另外一个名字,x称为a模n的逆元
*/
int modq(int a,int b,int n){//吉大模板
int d,x,y,ans;
d=ExtGcd2(a,n,x,y);
if(b%d!=0){
printf("No answer!\n");
}
else{
ans=(x*(b/d))%n;//如果b!=1,此时的x不为一
for(int i=0;i<d;i++){//共有d组结果
printf("%d - th answer: %d\n", i+1 , (ans+i*(b/d))%n );
}
}
}
/**
*欧拉函数(即小于n且与n互素的数的个数):Phi(n)
*欧拉函数的公式:Phi(n)=n*(1-1/p1)*(1-1/p2)...(1-1/pn)
*pi为小于等于n的素数且能够被n整除,对于这一点我们之需要判断到sqrt(n),
*在n中剔除能够被n整除的所有素数,如果n是素数,进行特判即可
*特别的:如果n是素数,那么Phi(n)=n-1
*
*/
int Phi(int n){
int res=n;
int k=(int) sqrt(n+0.5);
for(int i=2;i<=k;i++){
if(n%i==0){//第一个肯定是素数
res=res/i*(i-1);//ren=ren*(1-1/i)---》res=res*(i-1)/i
while(n%i==0){//剔除n的所有素数i
n/=i;
}
}
}
if(n>1){//根据唯一分解定理可知n必然为素数
res=res*(n-1)/n;
}
}
/**
*求出[1..n]的所有数的欧拉函数值:PhiTable(a,n)
*不用单独求每一个数的Phi,这里用到类似求素数的递推形式
*/
void PhiTable(int *phi,int n){
memset(phi,0,sizeof(int)*(n+1));
phi[1]=1;
for(int i=2;i<=n;i++){
if(!phi[i]) for(int j=i;j<=n;j+=i){//此时i肯定是素数,j也必然是i的倍数,
//此时j中必有j=j*(i-1)/i
if(!phi[j]){ //j第一次被处理,即i是就的第一个素因子
phi[j]=j;
}
//在phi(j)的结果中乘以(i-1)/i
phi[j]=phi[j]*(i-1)/i;
}
}
}
/**
*同余定理(大数取摸):
*(a+b)%n=((a%n)+(b%n))%n
*(a-b)%n=((a%n)-(b%n)+n)%n
*(a*b)%n=((a%n)*(b%n))%n
*(a/b)%n=(a%n)*(_b%n)%n
*
*在减法中(a%n)-(b%n)可能小于0,故加上n
*在除法中_b是b关于n的你逆元
*
*大数取摸运算abcd%n
*abcd%n=((((((a%n)*10+b)%n)*10+c)%n)*10+b)%n
*这里给出大数取摸的函数,用字符串表示大数
*/
int BigMod(char *s,int m){
int res=0;
int len=strlen(s);
for(int i=0;i<len;i++){
res=((res*10)%m+(s[i]-'0'))%m;
}
return res%m;
}
int DivMod(int a,int b,int n){//求(a/b)%n的值
//(a/b)%n=(a%n)*(_b%n)%n,_b是b的逆元,我们可以先求出逆元
//_bx与1模n同余--->bx-ny=1
int x,y;
ExtGcd2(b,n,x,y);
int _b=x;
cout<<_b<<endl;
while(_b<0){//保证逆元_b>0
_b+=n;
}
cout<<_b<<endl;
int ans=((a%n)*(_b%n))%n;
return ans;
}
/**
*幂取模:PowMod(a,n,m)
*求a^n%m的值,对于这个问题,当然我们可以用n次循环+逐步取摸解决
*这里我们来看一下快速幂取模的运算,即二分
*1、n=2*k a^n%m=((a^(n/2)%m)*(a^(n/2)%m))%m
*2、n=2*k+1 a^n%m=((a^(n/2)%m)*(a^(n/2)%m)*a)%m
*/
int QpowMod1(int a,int n,int m){//递归形式,当然也有迭代形式
int ans=1;
if(n==0) return 1;
int x=QpowMod1(a,n>>1,m);
ans=((x%m)*(x%m))%m;
if(n&1){//n=2*k+1
ans=(ans*(a%m))%m;
}
return ans;
}
int QpowMod2(int a,int n,int m){//bud
int ans=1;
int x=a;
while(n){
if(n&1) ans=ans*x%m;
x=((x%m)*(x%m))%m;
n=n>>1;
}
return ans;
}