最近学习数论来着,然后就萌生了一个整理一个数论题板子集合的想法

不过,会推数学式子才是数论题的关键,数学才是数论题的基础与核心

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

GCD:
原理: (a,b) = (b,a%b)

Code:

int gcd(int a,int b)
{
    if(a % b == 0)
    return a;
    return gcd(b,a % b);
}

Exgcd:

目的是求: ax + by = gcd(a,b)的一组解(x,y)

同时返回的是d = gcd(a,b)

Code:

int Exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    else
    {
        int d = Exgcd(b,a%b,x,y);
        int t = x;
        x = y;
        y = t - (a / b) * y;
    }
}

 

Miller_Rabin

这个算法是主要用来判断某一个数是不是质数的算法

但是请注意这个算法具有随机性,而且是单点判断,不适用于区间的素数筛选

这个算法的证明(手写):

一些数论题的板子 随笔 第1张

 

Code:

int gg[8] = {2,3,5,7,13,29,37,89};
Miller_Rabin(int a,int n)
{
    int d = n - 1;
    int r = 0;
    while(d % 2 == 0)
    {
        d /= 2;
        r++;
    }
    int x = kuaisumi(a,d,n);
    if(x == 1)
    return true;
    for(int i=0;i<r;i++)
    {
        if(x == n - 1)
        return true;
        x = (long long)x * x % n;
    }
    return false;
}
bool is_prime(int n)
{
    if(n <= 1)
    return false;
    for(int a=0;a<8;a++)
    if(n == gg[a])
    return true;
    for(int a=0;a<8;a++)
    if(!Miller_Rabin(gg[a],n))
    return false;
    return true;
}

 

线性筛:

线性筛的算法有很多种,但是本文这里为了简便起见

只介绍欧拉筛了,同时因为欧拉筛可以预处理莫比乌斯函数和欧拉函数等数论函数

还可以得出每一个合数的最小非1因子

好处多多a

Code:

memset(not_prime,0,sizeof(not_prime));
for(int i=2;i<=n;i++)
{
    if(!not_prime[i])
    {
        prime[++prime_cnt] = i;
        phi[i] = i - 1;
        mu[i] = -1;
    }
    for(int j=1;j<=prime_cnt;j++)
    {
        int x = i * prime[j];
        if(x > n)
        break;
        not_prime[x] = true;
        phi[x] = phi[i] * phi[prime[j]];
        mu[x] = mu[i] * mu[prime[j]];
        if(i % prime[j] == 0)
        {
            phi[x] = phi[i] * prime[j];
            mu[x] = 0;
            break;
        }
    }
}

 

快速幂:

这个的原理就是在实现的时候将每一个数将其"拆分"

从而我们可以用倍数来×代替了×多少次

Code:

int quickpow(int a,int b,int p)
{
    int res = 1;
    while(b)
    {
        if(b & 1)
        res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

 

 

中国剩余定理:

定理内容:

中国剩余定理的形式是这样的:

存在一个这样的式子:(中国剩余定理的限制条件:m1,m2,m3...mn这些数是互质的)

一些数论题的板子 随笔 第2张

 然后我们的任务是求最小的整数x使得非负整数x满足以上条件

我们设定一个 M = mi (即M为所有m的最小公倍数)

方程 M / mi * ti ≡ 1 (mod mi) 中 ti 为其最小非负整数解 (这里可以用exgcd来实现求解)

那么有一个解为 x = ∑ ai * M / mi * ti

通解为: x + i * M

特别地,算法的非负整数解为 (x % M + M) % M (将x移到[0,M]这个区间内)

算法证明:

因为M / mi 是除了mi之外的所有数的倍数

那么对于任意的k ≠ i 都有 ai * M / mi * ti ≡ 0 (mod mk)

又有M / mi * ti ≡ 1 (mod mi)

将两边同时乘ai得 ai * M / mi * ti ≡ ai (mod mi)

最后我们带入x = ∑ ai * M / mi * ti

从而原方程组成立

Code:

void exgcd(int a,int b,int &x,int &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return ;
    }
    exgcd(b,a % b,x,y);
    int t = x;
    x = y;
    y = t - a / b * y;
}
int crt()
{
    int ans = 0;
    int M = 1;
    int x,y;
    for(int i=1;i<=k;i++)
    M *= b[i];
    for(int i=1;i<=k;i++)
    {
        int t = M / b[i];
        exgcd(t,b[i],x,y);
        x = (x % b[i] + b[i]) % b[i];
        ans = (ans + t * x * a[i]) % M;
    }
    return (ans + M) % M;
}

 

拓展中国剩余定理

一些数论题的板子 随笔 第3张

 

这个与中国剩余定理不同的地方就在于这里的mi不一定两两互质了

解法:

我们假设已经求解出前k - 1个同余方程组的解为x

并且有M=∏(i1,k1)mi

那么前k个方程组的通解为 x + i * M(i ∈ Z)

对于我们即将插入第k个方程后形成的k个方程形成的方程组

我们就是要求一个正整数t,使得

x + t * M ≡ ak (mod mk)

我们针对于这一个式子转化一下就可以得到:

t * M ≡ ak - x (mod mk)

我们可以利用exgcd求解t

若这一个方程组无解t那么这整个方程组也就是无解的(显然,我们无法找到一个x使得x满足以上的方程成立条件)

若有,则前k个同余式构成的方程组的一个解为:
xk = x + t * M

所以我们整个算法的核心思路就是我们求解k次exgcd对于方程进行了k - 1次的展开

Code:

int exgcd(int a,int b,int &x,int &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b,a % b,x,y);
    int t = x;
    x = y;
    y = t - a / b * y;
    return d;
}
int excrt()
{
    int x,y,k;
    int M = b[1];
    int ans = a[1];
    for(int i=2;i<=n;i++)
    {
        int aa = M;
        int bb = b[i];
        int c = (a[i] - ans % bb + bb) % bb;  // x + t * M ≡ ak (mod mk) 
        int d = exgcd(aa,bb,x,y);  //求一组解 
        int m = bb / d;
        if(c % d != 0)  //若无解就直接返回 
        return -1;
        x = x * (c / d) % m;
        ans += x * M;
        M *= m; //要将这个mi加入到M里面 
        ans = (ans % M + M) % M;  // xk = x + t * M 
    }
    return (ans % M + M) % M; //返回值 
}

放两道模板题:

中国剩余定理

拓展中国剩余定理

未完待续···

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄