第十四周总结(初等数论)

欧几里得算法(辗转相除法)

本次数论的基础
先给出代码

int gcd(int a, int b)
{
    while (b != 0)
    {
        int c = a;
        a = b;
        b = c % b;
    }
    return a;
}

简单证明(不严谨,只是为了便于记忆):
a = kb + r
r = a % b
假设 c 为 a,b 的最大公约数
那么 c 可以整除 a,b,r 三者
因此 a,b 的最大公约数等于 b,r 的最大公约数
于是 gcd(a, b) = gcd(b, a%b)
当出现 gcd(a,0) 的情况时说明找到了最大公约数,为 a

扩欧(扩展欧几里得算法)

用于求解形如 ax + by = c (a,b,c均为整数)的方程的一组解
事实上,只有当 gcd(a,b) 能够整除 c 的时候方程才有解
因此通常情况下a,b是互质的,确保方程有解
先给出代码(默认c为1)

//在求解的同时返回a,b的最大公约数,建议将x y设置为全局变量
//最后的结果:x为a%b的逆元,y为b%a的逆元,结果可能会小于零,若小于零,手动x+=b,y+=a
int ex_gcd(int a, int b)
{
    if (b == 0)//b==0时得到gcd的结果,即a'=gcd(a,b)这里的a'和a是两个值,所以x=1
    {
        x = 1;
        y = 0;
        return a;
    }
    int ans = ex_gcd(b, a % b, x, y);
    //根据x',y'和x,y的关系逆推回去
    int t = x;
    x = y;
    y = t - (a / b) * y;
    return ans;//返回最小公约数
}

证明在这里插入图片描述

费马小定理求逆元

若有 a p 互质,且 p 为质数,则 ap-1 =1 (mod p)
因此 ap-2 = x 就是 a 在模 p 下的逆元
这个定理的限制较多,只有在p为质数的条件下才能使用。

线性打逆元表

如果要求一段连续数字的逆元,有一种线性的方法(会用板子就行,证明略)

long long f[maxn];
void work(long long n, long long p)
{
    f[1] = 1; // 1的逆元为1
    for (long long i = 2; i <= n; i++)
    {
        f[i] = -(p / i) * f[p % i];
        f[i] = (f[i] % p + p) % p;
    }
}

欧拉函数

φ(n)表示小于等于n的正整数中与n互质的数的个数,计算方法如图(p是x的所有质因数)
在这里插入图片描述

int Phi(int x)
{
    int ans = x;
    for (int i = 2; i * i <= x; i++)
    {
        if (x % i == 0) // i是x的一个质因数
        {
            ans = ans / i * (i - 1);
            while (x % i == 0)
                x /= i;
        }
    }
    if (x > 1) ans = ans / x * (x - 1);
    return ans;
}

φ(n)具有以下性质:
如果 m,n 互质,则 φ(nm) = φ(m)φ(n)

利用这个性质再结合素数筛的思想可以线性求一段数字的欧拉函数:

long long phi[maxn], n, pr[maxn], tot = 0, ans = 0;
bool pd[maxn];

void getphi(int n)
{
    phi[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (pd[i] = false)
        {
            pr[++tot] = i;
            phi[i] = i - 1; //质数除了1以外都与自己互质
        }
        for (int j = 1; j <= tot && pr[j] * i <= n; j++)
        {
            long long t = i * pr[j];
            pd[t] = true;
            if (i % pr[j] == 0)
            {
                phi[t] = phi[i] * pr[j];
                break;
            }
            else
                phi[t] = phi[i] * (pr[j] - 1);
        }
    }
}

组合计数

卢卡斯定理 a,b很大时(1e18),并且要求对结果取模p,有以下结论
在这里插入图片描述

int quick(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;
}
int C(int a, int b, int p)
{
    if (b > a) return 0;
    int res = 1;
    for (int i = 1, j = a; i <= b; i++, j--)
    {
        res = res * j % p;
        res = res * quick(i, p - 2, p) % p;
    }
    return res;
}
int lucas(int a, int b, int p)
{
    if (a < p && b < p) return C(a, b, p);
    return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值