初级数论

朴素欧几里得

用途:求公约数

//手写版
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
//系统函数版
int ans=__gcd(a,b);

扩展欧几里得

用途:解不定方程ax+by==gcd(a,b)的一组整数解

int exgcd(int a,int b,int &x,int &y){
    if(b==0){x=1,y=0;return a;}
    int xx,yy,d;
    d=exgcd(b,a%b,xx,yy);
    x=yy,y=xx-a/b*yy;
    return d;
}

解ax+by==c:
无解:
c%gcd(a,b)!=0
有解:
令d=gcd(a,b),原解为x’.
通解:x=x’*(c/d)+b/d,y=y’*(c/d)-a/d.
求x最小解:x=(x%b+b)%b.

中国剩余定理

用途:解同余方程组
已知有n个形如以下方式的式子:
X m o d W i = B i
(前提是所有的Wi必须两两互质)
设P为所有Wi的乘积,Mi为P/Wi

int China(int B[],int W[],int k){
    int d,x,y,ans=0,Mi,P=1;
    for(int i=1;i<=k;i++)P*=W[i];
    for(int i=1;i<=k;i++){
        Mi=P/W[i];
        d=exgcd(Mi,W[i],x,y);
        ans=(ans+x*Mi*B[i])%P;
    }
    if(ans>0)return ans;
    return (ans+P);
}

求乘法逆元

以下方法前提都是模数为质数
方法一:求某一个数的乘法逆元,时间复杂度O(n)

//求a在mod b前提下的逆元
int Inverse(int a,int b){
    int x,y;
    if(exgcd(a,b,x,y)==1)return (x%b+b)%b;
    return -1;//不存在逆元
}

方法二:线性筛,在O(n)时间复杂度内求出[1,n)内所有数的逆元

void Inverse(int n){
    inv[1]=1;
    for(int i=2;i<n;i++)inv[i]=(n-n/i)*inv[n%i]%n;
}

欧拉函数

用途:求phi(p)
时间复杂度:O(sqrt(p))

int phi(int p){
    int ans=p,a=p;
    for(int i=2;i*i<=a;i++)
        if(a%i==0){
            ans-=ans/i;
            while(a%i==0)a/=i;
        }
    if(a>1)ans-=ans/a;
    return ans;
}

欧拉筛

用途:求出[1,n]内所有数的phi(i)和这个范围内所有的质数
时间复杂度:O(n)

void getPhi(int n){
    int cnt=0;
    for(int i=2;i<=n;i++){
        if(!mark[i])prime[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=n && prime[j]*i<=n;j++){
            mark[i*prime[j]]=true;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }else phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}

Miller-Rabin素数测试

用途:判断一个数是否是质数;该算法是不确定算法,判断准确的概率为75%,但是判断10次左右就可以保证基本正确且正确率极高。
时间复杂度:log3n.

//#include<cstdlib>
bool Miiler_Rabin(int n){
    if(n==2)return true;
    if(n<2 || n%2==0)return false;
    int a,x,y,d=n-1,R=0;
    while(!(d&1))d>>=1,R++;//去掉因子2
    for(int i=1;i<=10;i++){
        a=rand()%(n-2)+2;
        //随机产生[2,n-1]内的底数
        x=mont(a,d,n);
        //a^d%n
        for(int j=1;j<=R;j++){
            y=x*x%n;
            if(y==1 && x!=1 && x!=n-1)return false;
            //x^2%n==1而x!=1 && x!=n-1,显然不符合条件
            x=y;
        }
        if(x!=1)return false;
    }
    return true;
}

快速乘

用途:用于long long范围内的乘法
注:尽量不要用,因为速度较慢

//计算(a*b)%c
ll quick_mul(ll a,ll b,ll c){
    ll ans=0;
    while(b){
        if(b&1)ans=(ans+a)%c;
        b>>=1,a=(a<<1)%c;
    }
    return ans;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80340673