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,即ab互质,我们假设a在模b意义下的逆元为x,则ax在模b时同余于1,即可得ax+by=1(应是ax=by+1,移项后就变为了axby=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)欧拉函数(不超过自己并与自己互质的数的个数)

φ(n)=n(11p1)(11p2)(11pk)

证明:

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

n=p1a1p2a2p3a3pkak

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

φ(n)=Sp1,p2,...pk(1)|s|npiϵSpi

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的完全剩余系就是{012...n1},而简化剩余系(也称缩系)就是完全剩余系中与n互素的那些数。

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

Z/nZn

,缩系记为

Zn

#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φ(n)1(modn)

因此a的逆元就是

aφ(n)1modn

如果n是素数,则

φ(n)=n1

所以a的逆就是pow_mod(a,n2,n)

2.模方程

(1)线性模方程

axb(modn)

把它转化为axny=b,当d=gcd(a,n)不是b的约数时无解,否则两边同时除以d,得到axny=b,a=ad,n=nd,b=bd,即为

axb(modn)

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

x(a)1b(modn)

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

axb(modn)

恰好有d个解,为p,p+n,p+2n,p+3n,...,p+(d1)n

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

那就用到了下面的定理

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

假设有方程组

xai(modmi)

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

用扩展欧几里得算法可以找到piqi使得wi×pi+mi×qi=1。然后令ei=wi×pi,则方程组等价于单个方程

xe1a1+e2a2++enan(modM)

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

证明:把等式wi×pi+mi×qi=1两边模mi后立即可得

ei1(modmi)

,而对于所有不等于ijwimj的倍数,因此

ei0(modmj)

,这样,x0mi取模时,除了eiai这一项余数为1×ai=ai之外,其余项的余数均为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为素数时,解模方程

axb(modn)

因为n是素数,只要a不为0,一定存在逆a1。根据欧拉定理,只需检查x=0,1,2,...,n1是不是解即可。因为

an11(modn)

,当x超过n1ax就开始循环了。

我们先检查前m(我们使mn12)项,即a0,a1,...,am1模n的值是否为b,并把aimodn保存在ei中,并求出am的逆am

下面考虑am,am+1,...,a2m1。这次不用一一检查,因为如果它们中有解,则相当于存在i使得

eiamb(modn)

,两边同乘am

eib(modn)

其中

b=amb(modn)

这样只需检验,是否有这样的ei等于b即可。

若没有,再考虑a2m,a2m+1,...,a3m1,故b=(a2m×b)modn

,所以我们一直要按照这样的方法枚举到am×m1

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   dolires  阅读(266)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示