数论小节

总结下数论中的一些简单的知识点,留着以后忘的时候查看,如有不足之处希望大家指出,我会更加努力学习。好了不说废话了,直接给出各个函数的代码吧。
/**
*@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;

}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值