数论学习小记 DAY1 基础数论

时间:2021-06-11 15:02:34

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

基础数论

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

先说几个简单的概念。

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

    • 传递性:若 a | b , b | c ,则 a | c .
    • a | b b | c x , y ,有 a | ( b × x + c × y ) .
    • m 0 ,则 a | b ( m × a ) | ( m × b ) .
    • 设整数x和y满足 a × x + b × y = 1 a | 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为两个整数,且他们的差 a b 能被某个自然数m整除,称a在模m意义下与b同余,记作 a b   ( m o d   m ) ,它意味着 a b = m × k ( k N + ) 。下面有几个常用性质:

    • 自反性,对称性,传递性,同加(乘/幂)性
    • a × b   m o d   k = ( a   m o d   k ) × ( b   m o d   k )   m o d   k .
    • a   m o d   p = x , a   m o d   q = x , g c d ( p , q ) = 1 , a   m o d   ( p × q ) = x
      • 证明:因为 a   m o d   p = x , a   m o d   q = x .
      • 则一定 s , t N ,使得 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   m o d   p × q = x .
    • a c b c   ( m o d   p ) g c d ( p , c ) = 1 , a b   ( m o d   p )
  • 最大公约数和欧几里得算法
    • 最大公约数:设 a = p 1 k 1 p 2 k 2 p 3 k 3 . . . p n k n , b = p 1 t 1 p 2 t 2 p 3 t 3 . . . p n t n p k , t N ,则 a b g c d ( a , b ) ( a , b ) = p 1 m i n ( k 1 , t 1 ) p 2 m i n ( k 2 , t 2 ) . . . p n m i n ( k n , t n ) ,该定义可以推广到多个数。
    • 欧几里得算法(辗转相除法): g c d ( x , y ) = g c d ( x , y x )
      • 证明:若 z | x , z | y , z | ( y x ) z ( y x ) x
      • 代码:
        int gcd(int x,int y)
        {
            return !y?x:gcd(y,x%y);
        } 

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

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

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

p × a + q × b = g c d ( a , b ) = g c d ( b , a % b ) = p × b + q × ( a % b ) = p × b + q × ( a a / b × b = q × a + ( p a / 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 = c a × x c   ( m o d   b ) ,有整数解的充要条件是 c % g c d ( a , b ) = 0
对于线性同余方程的一般形式 a × x + b × y = c ,我们只需要先求出一组方程 a × x 0 + b × y 0 = g c d ( a , b ) ,然后我们将等式的两边同时乘以一个 c g c d ( a , b ) ,就得出了原方程的一组解。

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

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

逆元

a × x 1   ( m o d   b ) , g c d ( a , b ) = 1 互质,则称x为a在模p意义下的逆元,记作 a 1 .

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

中国剩余定理

设自然数 m 1 , m 2 , m 3 . . . m r 两两互质,记 M = m 1 × m 2 × m 3 × . . . m r ,则同余方程组

{ x b 1   ( m o d   m 1 ) x b 2   ( m o d   m 2 ) x b 3   ( m o d   m 3 ) x b r   ( m o d   m r )

在模M同余意义下有唯一解
求解方法:
M i = M / m i , t i = M i 1 M i m i
在模M意义下,方程组的解为
( i = 1 n a i t i M i )   m o d   M

卡特兰数

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

f ( n ) = i = 0 n 1 f ( i ) × f ( n i 1 )

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

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

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

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

素数相关

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

素数的相关定理

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

素数的相关知识

  • 欧拉函数 φ ( n ) 表示小于等于n的数中与n互质的数的个数。于是有如下定理。
    • 欧拉函数是一个积性函数。
      • 若n是素数,则 φ ( n ) = n 1
      • 若n是某个素数的整次幂即 n = p a ,则 φ ( n ) = ( p 1 ) × p a 1
      • 若n是两个互质的数a,b的乘积即 n = a × b g c d ( a , b ) = 1 ,则 φ ( n ) = φ ( a ) × φ ( b )
    • n = p 1 a 1 × p 2 a 2 × . . . × p k a k ,则
      φ ( n ) = n × ( 1 1 p 1 ) × ( 1 1 p 2 ) × . . . × ( 1 1 p k )
    • 欧拉定理
      a φ ( p ) 1   ( m o d   p )
  • 素数的判定
    • 单个数
      • 穷举法:从 1 n 枚举并试除。复杂度差劲
      • 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的因数的概率是很小的。但是如果随机两个数并做差,那么概率将会大大提高。
    • 那设随机的两个数为 x 1 , x 2 ,使得 g c d ( | x 1 x 2 | , n ) > 1 的概率就会大。
    • 所以就有了如下算法:
      • 随机一个 x 1 [ 1 , n ] x 2 则通过公式 x 2 = ( l a s t _ x 2 2 + c ) % n 算出,其中c为任意给定值。
      • 若满足 g c d ( | x 1 x 2 | , n ) > 1 ,则证明 x 1 x 2 是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);
}