数论学习小记 DAY1 基础数论

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/szh_0808/article/details/80350520

省选两天出了两道数论,连暴力都不会打。回头一看确实自己的数论什么都不会。从头学起,开博客记录一下。立一个flag:半个月学完数论

基础数论

1.欧几里得和扩展欧几里得

先说几个简单的概念。

  • 整除:定义如下:设a是非零整数,b是整数,若存在一个整数q,使得 b=a×q 则称b可被a整除,记作a|b,称b是a的倍数,a是b的因数。下面有几个常用性质。

    • 传递性:若a|b,b|c,则a|c.
    • a|bb|c x,y,有a|(b×x+c×y).
    • m0,则a|b(m×a)|(m×b).
    • 设整数x和y满足a×x+b×y=1a|n,b|n,那么(a×b)|n.
      • 证明:因为a|n,b|n,所以
      • (a×b)|(b×n),(a×b)|(a×n) (性质三)
      • 所以(a×b)|(a×n×x+b×n×y)(性质二)
      • 其中a×n×x+b×n×y=n×(a×x+b×y)=n×1=n.得证。
    • b=q×d+c,那么d|b的充要条件是d|c
  • 同余:若a,b为两个整数,且他们的差ab能被某个自然数m整除,称a在模m意义下与b同余,记作ab (mod m),它意味着ab=m×k(kN+)。下面有几个常用性质:

    • 自反性,对称性,传递性,同加(乘/幂)性
    • a×b mod k=(a mod k)×(b mod k) mod k.
    • a mod p=x,a mod q=x,gcd(p,q)=1,a mod (p×q)=x
      • 证明:因为a mod p=x,a mod q=x.
      • 则一定s,tN,使得a=s×p+x,a=t×q+x
      • 所以s×p=t×q
      • 因为gcd(p,q)=1,故t中必有p这个质因子
      • 所以r,使s=r×q
      • 所以a=r×p×q+x,a mod p×q=x.
    • acbc (mod p)gcd(p,c)=1,ab (mod p)
  • 最大公约数和欧几里得算法
    • 最大公约数:设a=p1k1p2k2p3k3...pnkn,b=p1t1p2t2p3t3...pntnpk,tN,则abgcd(a,b)(a,b)=p1min(k1,t1)p2min(k2,t2)...pnmin(kn,tn),该定义可以推广到多个数。
    • 欧几里得算法(辗转相除法):gcd(x,y)=gcd(x,yx)
      • 证明:若z|x,z|y,z|(yx)z(yx)x
      • 代码:
        int gcd(int x,int y)
        {
            return !y?x:gcd(y,x%y);
        } 

进入正题,扩展欧几里得算法

扩展欧几里得算法是用来在已知a,b时,求解一组p,q,使得p×a+q×b=gcd(a,b)

如何求解呢?回想起刚才的gcd,我们列出如下等式。

p×a+q×b=gcd(a,b)=gcd(b,a%b)=p×b+q×(a%b)=p×b+q×(aa/b×b=q×a+(pa/b×q)×b)

int ex_gcd(int a,int b,int &x,int &y)
{
    if (!b)
    {
        x=1,y=0;
        return a;
    }
    int ret=ex_gcd(b,a%b,x,y);
    int tmp=x;x=y;y=tmp-a/b*y;
    return ret;
}
int main()
{
    int a,b,x,y,z;
    scanf("%d%d",&a,&b);
    z=ex_gcd(a,b,x,y);
    printf("%d %d %d\n",x,y); //x*a+y*b=gcd(a,b)
    return 0;
}

求解线性同余方程
定理:对于方程a×x+b×y=ca×xc (mod b),有整数解的充要条件是c%gcd(a,b)=0
对于线性同余方程的一般形式 a×x+b×y=c,我们只需要先求出一组方程a×x0+b×y0=gcd(a,b),然后我们将等式的两边同时乘以一个cgcd(a,b),就得出了原方程的一组解。

定理2:对于上述方程,若已知一组解(x0,y0),方程所有解为:x=x0+b×t,y=y0a×t,tN

应用定理2,我们就可以得出所有的解,在实际应用中,往往会求最小整数解,也就是最小化x,此时t=b/gcd(a,b),x=(x%t+t)%t.

逆元

a×x1 (mod b),gcd(a,b)=1互质,则称x为a在模p意义下的逆元,记作a1.

求逆元的几个方法:
- 扩展欧几里得求解:a×x1 (mod b),gcd(a,b)=1a×x+b×y=1,将上面的式子带入扩展欧几里得中,求出的x就是逆元
- 递推:A[i]=(p/i)A[p%i]。。。。证明略
- 费马小定理/欧拉定理。

中国剩余定理

设自然数m1,m2,m3...mr两两互质,记M=m1×m2×m3×...mr,则同余方程组

{xb1 (mod m1)xb2 (mod m2)xb3 (mod m3)xbr (mod mr)

在模M同余意义下有唯一解
求解方法:
Mi=M/mi,ti=Mi1Mimi
在模M意义下,方程组的解为
(i=1naitiMi) mod M

卡特兰数

卡特兰数是组合学中计数问题经常出现的数列,其前几项为1,2,5,14,42,132…
卡特兰数第n项求法:
1. 递归公式(1):

f(n)=i=0n1f(i)×f(ni1)

2. 递归公式(2):
f(n)=f(n1)×(4×n2)n+1

3. 组合公式(1):
f(n)=(2nn)n+1

4. 组合公式(2):
f(n)=(2nn)(2nn1)

常用模型:二叉树计数,括号序列,出栈序列

素数相关

素数定义:一个大于1的正整数,如果除了1和它本身之外,没有任何因数,则称其为质数或素数。1既不是质数也不是合数。素数有无穷多个。

素数的相关定理

  • 唯一分解定理
    • 若整数a2,那么a一定可以表示为若干个质数的乘积且唯一,形如
      a=p1p2p3p4...ps
  • 威尔逊定理
    • 若p为素数,则
      (p1)!1 (mod p)
      逆定理同样成立。
  • 费马小定理
    • 若p为素数,a为正整数,且gcd(a,p)=1,则
      ap11 (mod p)
    • 证明:易知a,2a,3a...(p1)a没有一个数是p的倍数,其次上述p-1个数中没有任何两个在模p意义下同余。
    • 故p-1个数对在模p意义下的同余是1…p-1的某一种排列,即
      a×2a×3a×...×(pa)a1×2×...×(p1) (mod p)
      化简得:
      ap1×(p1)!(p1)! (mod p)

素数的相关知识

  • 欧拉函数 φ(n)表示小于等于n的数中与n互质的数的个数。于是有如下定理。
    • 欧拉函数是一个积性函数。
      • 若n是素数,则φ(n)=n1
      • 若n是某个素数的整次幂即n=pa,则φ(n)=(p1)×pa1
      • 若n是两个互质的数a,b的乘积即n=a×bgcd(a,b)=1,则φ(n)=φ(a)×φ(b)
    • n=p1a1×p2a2×...×pkak,则
      φ(n)=n×(11p1)×(11p2)×...×(11pk)
    • 欧拉定理
      aφ(p)1 (mod p)
  • 素数的判定
    • 单个数
      • 穷举法:从1n枚举并试除。复杂度差劲
      • Miller-Rabin算法:一种基于随机化的检验质数的方法,选取合适的检验用数(2,7,61,24251)可以让失误率极极极极极极低。
const int prime[5]={0,2,7,61,24251};
ll quick_mul(ll a,ll b,ll Mod)
{
    ll ans=0;a%=Mod,b%=Mod;
    while (b)
    {
        if (b&1) ans+=a,ans%=Mod;
        b/=2,a+=a,a%=Mod;
    }
    return ans;
}
ll quick_pow(ll a,ll b,ll Mod)
{
    ll ans=1;a%=Mod,b%=Mod;
    while (b)
    {
        if (b&1) ans*=a,ans%=Mod;
        b/=2,a*=a,a%=Mod;
    }
    return ans;
}
bool check(ll r,ll m,ll x,ll a)
{
    ll res=quick_pow(a,m,x);
    if (res==x-1 || res==1) return 1;
    while (r--)
    {
        res=quick_mul(res,res,x);
        if (res==x-1) return 1;
    }
    return 0;
}
bool Miller_Rabin(ll x)
{
    if (x==2) return 1;
    if (x==1 || x%2==0) return 0;
    ll m=x-1,r=0;
    while (!(m&1))
        m/=2,r++;
    for (int i=1;i<=4;i++)
    {
        int a=prime[i];
        if (x==a) return 1;
        if (x%a==0) return 0;
        if (!check(r,m,x,a)) return 0;
    }
    return 1;
}
  • 筛法
    • 埃氏筛:将所有质数不大于n的倍数全部筛去,有冗余计算。
    • 线性筛:在适当的地方break,保证每个数只被筛去一次。
void get_prime(int n)
    visit[1]=1; 
    for (int i=2;i<=n;i++)
    {
        if (!visit[i])
            a[++cnt]=i;
        for (int j=1;j<=cnt && i*a[j]<=n;j++)
        {
            visit[i*a[j]]=1;
            if (i%a[j]==0)
                break;
        }
    }
}
  • Pollard Rho求大数质因子
    • 原理:基于随机的算法。
    • 我们知道,在[1,n]中随机一个数,使这个数是n的因数的概率是很小的。但是如果随机两个数并做差,那么概率将会大大提高。
    • 那设随机的两个数为x1,x2,使得gcd(|x1x2|,n)>1的概率就会大。
    • 所以就有了如下算法:
      • 随机一个x1[1,n]x2则通过公式x2=(last_x22+c)%n算出,其中c为任意给定值。
      • 若满足gcd(|x1x2|,n)>1,则证明x1x2是n的一个因数,若其是质数,则记录,否则递归的继续计算。
      • 判断素数使用Miller-Rabin算法。
const int prime[5]={0,2,7,61,24251};
vector <int> f;
ll quick_mul(ll a,ll b,ll Mod)
{
    ll ans=0;
    while (b) {if (b&1) ans+=a,ans%=Mod;a+=a;b/=2;a%=Mod;}
    return ans;
}
ll quick_pow(ll a,ll b,ll Mod)
{
    ll ans=1;
    while (b) {if (b&1) ans*=a,ans%=Mod;a*=a,b/=2,a%=Mod;}
    return ans;
}
ll gcd(ll a,ll b)
{
    return !b?a:gcd(b,a%b);
}
bool check(ll r,ll m,ll x,ll pri)
{
    ll res=quick_pow(pri,m,x);
    if (res==x-1 || res==1) return 1;
    while (r--)
    {
        res=quick_mul(res,res,x);
        if (res==x-1) return 1;
    }
    return 0;
}
bool Miller_Rabin(ll x)
{
    if (x==2) return 1;
    if (x==1 || !(x&1)) return 0;
    int m=x-1,r=0;
    while (!(m&1))
        m/=2,r++;
    for (int i=1;i<=4;i++)
    {
        int pri=prime[i];
        if (x==pri) return 1;
        if (!(x%pri)) return 0;
        if (!check(r,m,x,pri)) return 0;
    }
    return 1;
}
ll Pollard_Rho(ll n,ll c)
{
    ll _x1=rand()%n+1,_x2=_x1;
    ll i=1,k=2;
    while (1)
    {
        i++;
        _x1=(_x1*_x1+c)%n;
        ll t=abs(_x1-_x2);
        ll g=gcd(t,n);
        if (_x1==_x2) return n;
        if (g>1 && g!=n) return g;
        if (i==k)
            _x2=_x1,k*=2;
    }
}
void get(ll n)
{
    if (Miller_Rabin(n))
    {
        f.push_back(n);
        return ;
    }
    ll p=n;
    while (p>=n) p=Pollard_Rho(p,rand()%(n-1)+1);
    get(p);
    get(n/p);
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页