noip复习之数学(3)——数论

1.基本概念和常用代码

(1)素数(质数)

int prime[maxn],tot=0;
bool vis[maxn];
void init(int n)
{
    vis[1]=1;
    for(int i=2;i<=n;++i)
    {
        if(!vis[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&prime[j]*i<=n;++j)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;//每个数都只会被它最小的素数因子筛掉
        }
    }
}//线性筛素数

(2)欧几里得算法(辗转相除法)

#define ll long long
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}

扩展欧几里得算法

int exgcd(int a,int b,int &d,int &x,int &y)
{
    if(!b)
    {
        x=1,y=0;
        return d=a;
    }
    else
    {
        exgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}//可用于求ax+by=d的一组解,当且仅当d=gcd(a,b)时有解(若要通过此特解推出其他解,每次x减小b/d,y增加a/d即可)

​ 扩展欧几里得可以求逆元(逆元或许可以理解为在模意义下的倒数)

若要求\(a\)在模\(b\)意义下的逆元,首先\(a\)在模\(b\)意义下有逆元的前提条件是\(gcd(a,b)=1\),即\(a\)\(b\)互质,我们假设\(a\)在模\(b\)意义下的逆元为\(x\),则\(ax\)在模\(b\)时同余于\(1\),即可得\(ax+by=1\)(应是\(ax=by+1\),移项后就变为了\(ax-by=1\),我们令\(b=-b\),则\(ax+by=1\))

(3)快速幂

#define LL long long
LL pow_mod(LL a,LL b,int mod)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
        {
            ans=(ans*a)%mod;
        }
        a=(a*a)%mod;
        b>>=1;
    }
}

(4)欧拉函数(不超过自己并与自己互质的数的个数)

\[\varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_k}) \]

证明:

给出正整数n的唯一分解式

\[n=p_1^{a_1}p_2^{a_2}p_3^{a_3}\cdots p_k^{a_k} \]

用容斥原理,首先从总数\(n\)中减去\(p_1,p_2,p_3,\cdots,p_k\)的倍数的个数(\(p_i\)都是素数,故不是其倍数即为与其互质),即\(n-\frac{n}{p_1}-\frac{n}{p_2}-\cdots-\frac{n}{p_k}\)(设\(p_i\)最大的满足小于等于\(n\)的倍数为\(p_i\times t\),故求得\(t\)为$\frac{n}{p_i}),然后加上“同时是两个数的倍数的数,再减去同时是三个数的倍数的数,...,最后得到的公式即为:

\[\varphi(n)=\sum_{S\subseteq p_1,p_2,...p_k}(-1)^{|s|}\frac{n}{\prod_{p_i\epsilon S}}p_i \]

int phi[maxn];
void init(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;++i)
    {
        phi[i]=i;
    }
    for(int i=2;i<=n;++i)
    {
        if(phi[i]==i)
        {
            for(int j=i;j<=n;j+=i)
            {
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
}//筛表法求欧拉函数

(5)剩余系,模乘法的逆

通俗的说,模n的完全剩余系就是\(\{0,1,2,... ,n-1\}\),而简化剩余系(也称缩系)就是完全剩余系中与\(n\)互素的那些数。

模n的完全剩余系最常见的写法是\(Z/nZ\),也可以写成:

\[Z/n或者Z_n \]

,缩系记为

\[Z_n^* \]

#define LL long long
LL inv(LL a,LL mod)
{
    LL d,x,y;
    return exgcd(a,mod,d,x,y)==1?(x+mod)%mod:-1;
}//模乘法的逆

求逆的另一方法是利用欧拉定理

给定任意的整数\(n>1\),对于\(n\)的缩系中的任意一个元素\(a\),

\[a^{\varphi(n)}\equiv1(mod\quad n) \]

因此\(a\)的逆元就是

\[a^{\varphi(n)-1}mod\quad n \]

如果n是素数,则

\[\varphi(n)=n-1 \]

所以\(a\)的逆就是\(pow\)_\(mod(a,n-2,n)\)

2.模方程

(1)线性模方程

\[ax\equiv b(mod\quad n) \]

把它转化为\(ax-ny=b\),当\(d=gcd(a,n)\)不是\(b\)的约数时无解,否则两边同时除以\(d\),得到\(a'x-n'y=b',a'=\frac{a}{d},n'=\frac{n}{d},b'=\frac{b}{d}\),即为

\[a'x\equiv b'(mod\quad n') \]

此时\(a'\)\(n'\)已经互素,因此再左乘\(a'\)在模\(n'\)意义下的逆元,则解为:

\[x\equiv (a')^{-1}b'(mod\quad n') \]

这个解是模n'剩余系中的一个元素,但我们还需要把它表示为模n剩余系中的元素。\((a')^-1 b'=p\),上述解相当于\(x=p,x=p+n',x=p+2n',x=p+3n'\),...。对于模n来说,假设\(p+in'\)\(p+jn'\)同余,\((p+in')-(p+jn')=(i-j)n'\)\(n\)的倍数,因此,\((i-j)\)\(d(gcd(a,n))\)的倍数。换句话说,在模\(n\)剩余系下,

\[ax\equiv b(mod\quad n) \]

恰好有\(d\)个解,为\(p,p+n',p+2n',p+3n',...,p+(d-1)n'\)

如果有多个方程,变量还是只有一个,又该怎么做呢?

那就用到了下面的定理

(2)中国剩余定理(Chinese Remainder Theorem)

假设有方程组

\[x\equiv a_i(mod\quad m_i) \]

且所有的mi两两互素。令\(M\)为所有\(m_i\)的乘积,\(w_i=M/m_i\),则\(gcd(w_i,m_i)=1\)

用扩展欧几里得算法可以找到\(p_i\)\(q_i\)使得\(w_i \times p_i+m_i\times q_i=1\)。然后令\(e_i=w_i\times p_i\),则方程组等价于单个方程

\[x\equiv e_1a_1+e_2a_2+\cdots +e_na_n(mod\quad M) \]

,即在模\(M\)剩余系下,原方程组有唯一解。

证明:把等式\(w_i\times p_i+m_i\times q_i=1\)两边模\(m_i\)后立即可得

\[e_i\equiv 1(mod\quad m_i) \]

,而对于所有不等于\(i\)\(j\)\(w_i\)\(m_j\)的倍数,因此

\[e_i\equiv 0(mod\quad m_j) \]

,这样,\(x_0\)\(m_i\)取模时,除了\(e_ia_i\)这一项余数为\(1\times a_i=a_i\)之外,其余项的余数均为\(0\)

#define LL long long
LL crt(int n,int* a,int* m)
{
    LL M=1,d,x,y,x=0;
    for(int i=1;i<=n;++i) M*=m[i];
    for(int i=1;i<=n;++i)
    {
        LL w=M/m[i];
        exgcd(m[i],w,d,d,y);
        x=(x+y*w*a[i])%M;
    }
    return (x+M)%M;
}

(3)离散对数

为了简单起见,我们只考虑一种最简单的情况,即当\(n\)为素数时,解模方程

\[a^x\equiv b(mod\quad n) \]

因为\(n\)是素数,只要\(a\)不为\(0\),一定存在逆\(a^{-1}\)。根据欧拉定理,只需检查\(x=0,1,2,...,n-1\)是不是解即可。因为

\[a^{n-1}\equiv 1(mod\quad n) \]

,当\(x\)超过\(n-1\)\(a^x\)就开始循环了。

我们先检查前\(m\)(我们使\(m\)\(n^{\frac{1}{2}}\))项,即\(a^0,a^1,...,a^{m-1}\)模n的值是否为\(b\),并把\(a^imodn\)保存在\(e_i\)中,并求出\(a^m\)的逆\(a^{-m}\)

下面考虑\(a^m,a^{m+1},...,a^{2m-1}\)。这次不用一一检查,因为如果它们中有解,则相当于存在i使得

\[e_i*a^m\equiv b(mod\quad n) \]

,两边同乘\(a^{-m}\)

\[e_i\equiv b'(mod\quad n) \]

其中

\[b'= a^{-m}b(mod\quad n) \]

这样只需检验,是否有这样的\(e_i\)等于\(b'\)即可。

若没有,再考虑\(a^{2m},a^{2m+1},...,a^{3m-1}\),故\(b''=(a^{-2m}\times b)mod n\)

,所以我们一直要按照这样的方法枚举到\(a^{m\times m-1}\)

int pow_mod(int a,int b,int mod)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int log_mod(int a,int b,int n)
{
    int m,v,e=1;
    m=(int)sqrt(n+0.5);
    v=pow_mod(pow_mod(a,m,n),n-2,n);
    map<int,int> x;
    x[1]=0;
    for(int i=1;i<m;++i)
    {
        e=(e*a)%n;
        if(!x.count(e)) x[e]=i;
    }
    for(int i=0;i<m;++i)//考虑a^(im),a^(im+1),...,a^(im+m-1)
    {
        if(x.count(b)) return i*m+x[b];
        b=(b*v)%n;
    }
    return -1;
}//这就是可用于解决离散对数的大步小步算法(Baby_Step_Giant_Step Algorithm),复杂
//度O(n^(1/2)logn)

posted on 2019-10-01 21:03  dolires  阅读(282)  评论(0)    收藏  举报

导航