数论在ACM中的应用

转自:http://blog.csdn.net/danliwoo/article/details/48827813#t22

整除与剩余

整除在自然数范围内的引入是十分自然的,即要把一个整体平均分为若干份。若要求分得的结果必须是整数,则可能存在多余的情况,即余数。

ababab

整数是对自然数的扩充,引入了负数的概念。负的余数是一个数学概念,在实际情形中说剩余的东西是负数,往往是可笑的。因为那意味着并没有剩余。利用同余,可以给出这类情况的意义。

整除判断多利用代数恒等变形,可尝试证明如下两题: 
1. 设 a>1,m,n>0 , 证明: (am1,an1)=a(m,n)1.  
2. 设 a>b,gcd(a,b)=1 , 证明: (ambm,anbn)=a(m,n)b(m,n).

同余,即除同一个数得到相同的余数。例如 8=15+313=25+3 ,可知8,13关于5同余,一般可以记作: 813(mod5)

若a和b模d同余,则下列命题等价: 

abdn使a=b+ndd(ab)

当a是一个负数时,则将n置为负数。

几个看两眼的定理:

  • ab(modm)dm,ab(modd)
  • ab(modm),(a,m)=(b,m) (辗转相除法);
  • 1in,ab(modmi)ab(mod[m1,m2,...,mn])
  • acbc(modm),(c,m)=d,ab(modmd)

对于相同的模,同余式可以加、减、乘,可以通过上述等价的命题快速证明。在计算整数幂的同余式时可以根据乘法性质对小指数的先进行模运算,再代回原式,可以减小一定的计算量。例如计算 2201513 ,可以先得到 

24163(mod13)
283294(mod13)
212121(mod13)
2201521148327(mod13)

有些题目答案过大时,往往要求输出mod一个大数N的答案。可以在中间过程中先mod N,避免超int。

某些数被模除时具有特别的性质,下面看一道自己出的题: 
题目大意:从 nm(1nm108) 之间有多少个数满足奇数位上的数字之和减去偶数位上的数字之和为2。 
由于数据范围不大,可以暴力试试看。机智的会用数位DP。而出题人其实是从数论的角度出发的。已知: 

102k100k(911+1)k1(mod11)
102k+1100k10101(mod11)

对于任意的正整数  N=a2na2n1a2a1¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯(a2na2n10 ,并且设x = 奇数位之和-偶数位之和,则原正整数都可拆解为:
a2na2n1...a2a1¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯=i=12nai10i=i=1na2i1102i1+i=1na2i102i
i=1na2i11+i=1na2i(1)xd(mod11)
现在要求x=2,则d=2,则 Nd2(mod11) ,可知满足题意的数都有这个性质,再进行判断。但要注意的是d=2时,x不一定等于2,比如13,-9等都能得到d=2. 因此对于mod 11为2的数中仍要进行筛查。


素数问题

基本描述

素数仅能被1和自身整除(除1外),可以看作是组成整数的基本单位。 
要知道一个整数的阶乘能被素数p整除多少次,即p的幂为多少次,可利用:

[np]+[np2]+[np3]+...+[npt]

其中 t=logpn.

素数定理:定义 π(x) 为1~x内素数的个数(即欧拉函数),则:

limxπ(x)xln(x)=1xπ(x)xln(x)

由此可以估算出在1~x范围内素数的个数。

素数的形成没有成型的规律,但有许多相关的猜想。

  • 伯特兰猜想: n>1,p,s.t.n<p<2n
  • 孪生素数:存在无穷多素数对p,p+2.
  • 哥德巴赫猜想:任何偶数都可以拆分成两个素数的和。
  • 构造猜想:存在无穷多个素数满足 p=n2+1

素数筛法

  • 埃拉托斯尼斯筛法 
    先将2~N内所有的数标记为素数,从最小的素数开始筛去其倍数,再找到下一个素数,依次筛去非素数的数,剩余的即为素数。 
    需要筛的素数范围只需在2~ N .
int num[N] = {0,0,1}, prime[N] = {2}, pr = 1;
void set()
{
    for(int i = 2;i < N;i++)
        num[i] = 1;
    for(int i = 2;i <= sqrt((double)N);i++)
        if(num[i]) for(int j = i*i;j < N;j += i)
            num[j] = 0;
    for(int i = 3;i < N;i += 2)
        if(num[i])
            prime[pr++] = i;
};
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在POJ 2689,可以节省空间大小。

  • 6N ± 1筛法 
    由于6N,6N+2,6N+3,6N+4(N>0)均不是素数,故在筛素数时可以直接从6N ± 1中判断和筛选素数。

素数判定

  • 朴素的暴力判断算法 O(n)  
    从0~ n 依次尝试能否整除
  • 素数筛法后直接判定 O(1)
  • Lucas-Lehmer判定法(只对梅森数1作用) 
    设p是素数,第p个梅森数为 Mp=2p1r1=4. k2,rkrk122(modMp),(0rk<Mp) ,得到序列{ rk
    Mp  rp10(modMp)
  • Miller-Robin测试 
    判断n是否为素数 
    • 由费马小定理,取与n互质的a作为底数,验证 an11(modn) 是否成立。
    • 当上式成立时,n是基于a的伪素数。
    • 多取几个小于n的不同的a,反复验证,若都成立,则认为n是素数。取5次即可保证比较高的正确率。
    • 不适用范围:卡麦克尔数2。
  • 二次探测 
    对卡麦尔数(伪素数)进行测试

二次探测定理:对素数 p,x21(modp) 的小于p的正整数解 x1p1 .

对n-1除2,检验 an121 or n1(modn) 是否成立,一旦不成立则可认定n不是素数。反复除2直到除不尽为止。

//从n-1的最大奇因子为指数开始判定
bool recheck(LL a, LL n, LL m, LL j)
{
    LL d = po(a, m, n);//对大数运算时里面的乘法要另外算mult_mod
    if(d == 1 || d == n-1)
        return true;
    for(int i = 0;i < j;i++)
    {
        d = d*d % n;
        if(d == 1 || d == n-1)
            return true;
    }
    return false;
}
bool Miller_Rabin(LL n)
{
    if( n < 2)return false;
    if( n == 2)return true;
    if( (n&1LL) == 0)return false;//偶数
    srand((unsigned) time (NULL));
    LL m = n-1, j = 0;
    while( !(m & 1LL) )
    {
        m >>= 1;
        j++;
    }
    for(int i = 0;i < 5;i++)
    {
        LL a  = rand()%(n-2) + 2;
        if( !recheck(a,n,m,j) )
            return false;
    }
    return true;
}
   
   
   
   
  • 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
  • 32
  • 33
  • 34
  • 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
  • 32
  • 33
  • 34

剩余系配对

素数p的完全剩余系为0~p-1,其中 12(p1)21(modp) ,其他的数2,3,...,p-2可以两两配对 (i,j) ,满足 ij1(modp) .可以先从中任取一个数a,方程 ax1(modp) 必有唯一解。


整数分解

将整数化为质因子的幂的乘积的唯一形式。常用试除法、筛选法,比较简单不举例了。对于非常大的数而言,两者用处不大。

Pollard  ρ  整数分解法

原理还不太懂: 
1. 生成两个整数a、b,计算p=(a-b,n),直到p不为1或a,b出现循环为止。 
2. 若p=n,则n为质数;否则为n的一个约数。

分解n的具体步骤如下: 
1. 选取一个小的随机数 x1 ,迭代生成 xi=x2i1+k ,取k=1,若序列出现循环则退出。 
2. 计算 p=gcd(xi1xi,n) 直到p>1,否则返回上一步。 
3. 若p=n,则n为素数。否则p为n的约数,继续分解p和n/p

//找出一个因子
LL pollard_rho(LL x,LL c)
{
    LL i = 1, k = 2;
    srand(time(NULL));
    LL x0 = rand()%(x-1) + 1;
    LL y = x0;
    while(1)
    {
        i++;
        x0 = (mult_mod(x0,x0,x) + c)%x;//迭代公式为(x0*x0+c)%x
        LL d = gcd(y - x0,x);//gcd返回一个绝对值
        if( d != 1 && d != x)
            return d;
        if(y == x0)
            return x;
        if(i == k)
        {
            y = x0;
            k += k;
        }
    }
}

//对n进行素因子分解,存入factor. k设置为107左右即可
void findfac(LL n,int k)
{
    if(n == 1)
        return;
    if(Miller_Rabin(n))
    {
        factor[tol++] = n;
        return;
    }
    LL p = n;
    int c = k;
    while( p >= n)
        p = pollard_rho(p,c--);//c即k表示最大搜索次数?
    //值变化,防止死循环k
    findfac(p,k);
    findfac(n/p,k);
}
   
   
   
   
  • 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
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 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
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

如POJ 1811 1061


扩展欧几里得

辗转相除法可以来求a,b两个数的最大公约数。而辗转相除的过程中,附带也可以得到满足 ax+by=c() 的解系 (X,Y)

先来解决 ax+by=gcd(a,b)() 这一特殊情况。

  1. 最简情况: b=0 时, gcd(a,b)=a ,方程化为 ax=a ,则一组特殊解为 (1,0)
  2. b0 时,由辗转相除法
    gcd(a,b)=gcd(b,a%b)
    可以改写原方程得到 
    bx+(a%b)y=gcd(b,a%b)
    a%b=aab×b ,代入前一方程,得到 
    ay+b(xaby)=gcd(a,b)
    与方程(*)对比 ax+by=gcd(a,b) ,可知: x=yy=(xaby) 得到一组递推关系。
  3. 方程依次向下递归可以得到最简情况,得到解(1,0),再由递推关系回代到上一组解,最终可以得到方程(*)的解。 注意到,这是一组特解,并不唯一。
//解方程ax+by=gcd(a,b) 返回gcd(a,b) 得到一组特解(x,y)
int extend_Euclid(int a, int b, int &x, int &y){
    if(b==0){
    x = 1; y = 0;
    return a;
    }
    int r = extend_Euclid(b, a%b, y, x);
    y -= a/b*x;
    return r;
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 回到方程 () 首先,判断 gcd(a,b) 是否是c的约数。若非,则方程无解。

  • 下面考虑  gcd(a,b)c ,则方程有解。 
    由扩展欧几里得算法算出方程 ax+by=gcd(a,b) 的特解 (x0y0)  
    原方程 () 的特解 (X0Y0)=(x0,y0)cgcd(a,b)  
    为保证整数解则 ΔY=aΔXbZ,ΔX=bgcd(a,b)  
    原方程 () 解系 {X=X0+bgcd(a,b)K\[2ex]Y=Y0agcd(a,b)KK

  • K0[gcd(a,b)1] ,求出方程所有解中的代表元素共有 gcd(a,b) 个。是X模b意义下的所有解。其中最小非负整数解 x1=(x0cgcd(a,b)%ran+ran)%ran,ran=bgcd(a,b) (在模ran范围内有唯一的解) x1 是模ran范围内最小的正整数解,又 ranbb 范围内最小的正整数解。

  • 注意:解系中解的间隔与c无关。也可以从坐标系上的直线看出来。直线与网格的交点为一组整数解,c只决定了直线的平移位置,a,b决定了斜率。

拉梅定理:用欧几里得算法计算两个正整数的最大公因子时,所需的除法次数不会超过两个整数中较小的那个十进制数的位数的5倍。

推论:求两个正整数 a,b,a>b 的最大公因子需要 O(log2a)3 次的位运算。

扩展欧几里得的应用 
主要有:求解不定方程、模的逆元、同余方程。如POJ 1061,只要化为 ax+by=c 的形式即可求解。


解二元一次线性方程

注意:为保证gcd(a,b)为正,要让a,b均为正,避免错误,此时解应该反号。

//求解ax+by=c 返回0为无解,否则返回gcd(a,b)个解,用X[],Y[]存;
int X[N], Y[N];
int equation(int a, int b, int c)
{
    int x, y;
    int g = extend_Euclid(a, b, x, y);
    if(c % g)
        return 0;    //表示无解
    x *= c/g, y *= c/g;
    for(int k = 0;k < g;k++)
    {
        X[k] = (x+b/g*k)%b;
        Y[k] = (c-a*X[k])/b;
    }
    return g;
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

解一元线性同余方程

Thm  对于方程 axb(modm) ,其中 a,b,mZ and m>0,(a,m)=d.db, 则方程恰有d个模m不同余的解。否则方程无解。

容易发现,方程 axb(modm) 可以写为方程 ax+my=b 的形式,则利用前面得到的结论就很容易理解了。

//求解ax=b(mod m) 返回0为无解,否则返回gcd(a,m)个mod m意义下的解,用X[]存
int mod(int a, int b, int m)
{
    return equation(a, m, b);
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

求逆元

特别地,当有 (a,m)=1ax1(modm) 得到的x即是a模m的逆。


解一元线性同余方程组

当有方程组 

xb1(modm1)xb2(modm2)...xbn(modmn)
可知每个方程必有满足的一个特解,并存在模 mi 的解系 
{x=b1+m1y1x=b2+m2y2
相减后得到: 
m1y1m2y2=b2b1
化为二元一次线性方程,得到特解 y01 ,再化为模 lcm(m1,m2) 意义下的特解(保证了在原来两个方程的解系中),代入到第一个方程,则两个方程等价为
xb1+m1y10(modlcm(m1,m2))
再继续进行两两运算,直至最后得到满足的答案。

//在模m范围内只有可能有一个解或无解 要求最小整数解时equation()内只需保存一个解代回
LL X[N], Y[N];
int equation(int a, int b, int c)
{
    int x, y;
    int g = extend_Euclid(a, b, x, y);
    if(c % g)
        return 0;    //表示无解
    int ran = b/g;
    X[0] = (c/g*x%ran+ran)%ran;    //只需存最小整数解
    return g;
}
int mo[N], bo[N];
int eq_set()
{
    int n, b1, m1, b2, m2, m, r = 1;
    scanf("%lld", &n);
    for(int i = 0;i < n;i++)
        scanf("%lld", &mo[i]);
    for(int i = 0;i < n;i++)
        scanf("%lld", &bo[i]);
    m1 = mo[0], b1 = bo[0];
    for(int i = 1;i < n;i++)
    {
        m2 = mo[i], b2 = bo[i];
        r = equation(m1, m2, b2-b1);
        if(!r)
            return -1;  //返回无解
        b1 += m1*X[0];
        m1 *= m2/r;
        b1 %= m1;
    }
    return (b1%m1+m1-1)%m1+1; //返回正整数解,0被化为最小公倍数
}
   
   
   
   
  • 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
  • 32
  • 33
  • 34
  • 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
  • 32
  • 33
  • 34

如POJ 1006,由题意可列出满足的方程组,再求出相应的答案。当结果为非正数时利用同余性质化为正数。


中国剩余定理

Thm m1,m2,...,mr 是两两互素的正整数,则同余方程组(同上面的解一元线性同余方程组), xai(modmi) 有模 M=m1m2...mn 的唯一解。

Ni=Mmi,(Ni,mi)=1,xi, Nixi+miyi=1,Nixi1(modmi) ,易得

i=1raiNixiai(modmi)
即左式为方程组的解。

//解方程组x=ai(mod mi) mi之间两两互质
int China(int r)
{
    int M = 1, ans = 0;
    for (int i = 0; i < r; ++i)
        M *= m[i];
    for(int i = 0;i < r;i++)
    {
        int N = M/m[i];
        int x, y;
        extend_Euclid(N, m[i], x, y);
        ans = (ans+a[i]*N*x)%M;
    }
    ans = (ans - a[r])%M;
    return (ans+M)%M;
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

解多元线性同余方程

Thm  多元线性同余方程 a1x1+a2x2+...+anxn+b0(modm) 有解 (a1,a2,...,an,m)b  
若有解,则解(模m的剩余类)的个数为 mn1(a1,a2,...,an,m)

d1=(a1,a2,...,an1,m),d=(a1,a2,...,an1,an,m), d=(d1,an)d1m ,由同余性质,可得

a1x1+a2x2+...+an1xn1+anxn+b0(modd1)
d1a1x1+a2x2+...+an1xn1 ,所以
anxn+b0(modd1)

你可能感兴趣的:(模板,干货,专题,数论)