算法基础系列第四章——数论之从欧拉卷到欧几里得

友友们好(^-^)🌹🌹🌹,我是杨枝,一枚在算法领域迈步的尽心博主,目前还是一只纯纯的菜汪🐶。 典型的又菜又爱闹那种👀,做不好很多事,说不好很多话,写题还总不Ac😅,还在努力还在前进👣。 你们对我来说都是是独一无二的💓。 时刻谨记:认真写算法,用心去分享。不负算法,不误卿。 感谢相遇(^㉨^)

💓欧拉函数

🌟概念引入

1. 互质: 如果两个数的最大公约数是1,则称这两个数互质。
2. 欧拉函数: 在1~N中与N互质的数的个数被称为欧拉函数,记作:φ(N) 假如对这个正整数按照算术基本定理分解,就可按照下图的流程,推导出欧拉函数的计算公式

欧拉函数证明

看起来就脑袋痛,对吧。但是顺着我圈的地方去读,会感觉,呀,就这呀,就这呀,豁然开朗喔~

都会好的

💓公式法求欧拉函数

🌟算法模板

实现流程图:
欧拉函数
算法的代码描述如下:

公式法求欧拉函数
int phi(int x)
{
    int res = x;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);
    
    return res;
}

🌟例题描述

欧拉函数
🎇🎇🎇原题传送门🎇🎇🎇

🌟参考代码(C++版本)

🎇🎇🎇点击这里查看参看代码喔QWQ🎇🎇🎇

🌟疑难点剖析

一、分解质因数是基础

对分解质因数的算法模板要清楚,假如对分解质因数比较模糊的小伙伴可以看看这篇文章喔,也是挂过热榜的,值得信赖呀

🎇🎇🎇由理论推导到代码实践逐步精准掌握数论 (一)🎇🎇🎇

值得信赖
二、欧拉函数的计算公式

记得住最好,假如记不住了,可以利用容斥原理,自己推理两三个数据就可以发现规律喔

让我好好想想

💓线性筛法求欧拉函数

公式法求欧拉函数适用于只求少量数据的欧拉值,倘若题目要求很多数据的欧拉值或者欧拉值的乘积、和。那么公式法就可能TLE,因此,借助线性筛法的模板来优化求公式法求欧拉值的过程。

🌟算法模板

算法实现流程图:
线性筛法求欧拉值

算法模板代码实现:

筛法求欧拉函数
int primes[N], cnt;		// primes[]存储所有素数
int phi[N];			// 存储每个数的欧拉函数
bool st[N];			// st[x]存储x是否被筛掉


void get_eulers(int n)
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            int t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0)
            {
                phi[t] = phi[i] * primes[j];
                break;
            }
            phi[t] = phi[i] * (primes[j] - 1);
        }
    }
}

🌟例题描述

筛法求欧拉函数
🎇🎇🎇原题传送门🎇🎇🎇

🌟参考代码(C++版本)

🎇🎇🎇点击这里查看参看代码喔QWQ🎇🎇🎇

🌟疑难点剖析

一、线性筛法的难点在于如何结合公式统计出当前参数的欧拉值,将相应结果存放到phi数组中。
情况一:当前这个数据是质数。 与质数n互质的数的个数有n-1个。比如质数7。1~6与它的最大公约数都是1,互质的个数就是6。因此可以得到phi[n] = n-1;
情况二:筛选中,当前这个质数primes[j]是i的质因数 phi[primes[j] * i] 可变型为phi[primes[j] ] * phi[i] 。因为primes[j]是i的质因数,所以在用公式算phi[i]的时候就已经把(1-1/primes[j])算了。所以对于phi[primes[j]]就只用将剩下的primes[j]乘上。 因此 phi[primes[j] * i] = phi[i]*primes[j];
情况三:筛选中,当前这个质数primes[j]不是i的质因数。 因为primes[j]不是质因数了,就得独立的对它进行公式计算,即phi[ primes[j] ] = primes[j] x (1 - 1/primes[j]) x phi[i] 因此,结果为: phi[primes[j] * i] = phi[i]*(primes[j]-1);
二、注意范围,因为题目所给的数据就能很大,统计欧拉值的时候以乘积的形式统计的,可能会造成溢出。对于数论的题,可以泛泛而谈大多数时候都是要用long long 类型的,因为所给的数据一般比较大,比如2 x 10^9。假如再进行乘法运算,就会溢出

伤口撒盐

💓快速幂

快速幂也叫欧拉降幂、反复平方法。是数论中常客的常客了。 使用背景:一般看到幂运算的时候就可以考虑把快速幂拿出来了。

快速幂的时间复杂度是O(logn),举个栗子,假如要处理109的数据,大概只需要运算30次,比O(n2)的效率高了很多个档次了。

新的知识增加了
快速幂算法的原理说简单一点,就是把幂二进制化,比如对于x22
22的二进制是(10110)2

那么对于x22按照快速幂的原理的拆分法,拆出来就是下面的效果:
x22 = x16 * x4 * x2

基本了解的算法怎么实现的,那么下面咱们具体从例题中落实吧

🌟算法模板

幂运算实现流程:
快速幂

幂运算代码实现:

快速幂
求 m^k mod p,时间复杂度 O(logk)。

int qmi(int m, int k, int p)
{
    int res = 1, t = m;
    while (k)
    {
        if (k&1) res = res * t % p;
        t = t * t % p;
        k >>= 1;
    }
    return res;
}

🌟例题描述

快速幂
🎇🎇🎇原题传送门🎇🎇🎇

🌟参考代码

🎇🎇🎇点击这里查看参看代码喔QWQ🎇🎇🎇

🌟疑难点剖析

小心int溢出

因为快速幂中是指数函数彼此之间的乘积运算。当数据十分庞大的时候,int可能装不下,所以要强转为long long类型
    if(k&1) res = (LL)res*a%p;
    k>>=1;
    a=(LL)a*a%p;

💓拓展欧几里得算法

🌟前提引入

拓展欧几里得算法是在欧几里得算法的基础上,证明裴蜀定理而产生的的。

裴蜀定理:
对于任意整数a,b,存在一对整数x,y,满足 ax + by = gcb(a,b)

下图是李煜东老师书中对定理证明的详细步骤。后续算法的代码落实也是依赖于这个证明中进行公式变换的部分

拓展欧几里得

🌟算法模板

算法实现流程
扩展欧几里得算法实现流程
算法代码描述:

扩展欧几里得算法

// 求x, y,使得ax + by = gcd(a, b)
int exgcd(int a, int b, int &x, int &y)
{
    if (!b)
    {
        x = 1; y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a/b) * x;
    return d;

🌟例题描述

l例题描述
🎇🎇🎇原题传送门🎇🎇🎇

🌟参考代码(C++版本)

🎇🎇🎇点击这里查看参看代码喔QWQ🎇🎇🎇

🌟疑难点剖析

注意要拓展欧几里得算法实现的时候,在函数参数中,要传入系数x和y的引用
注意
豪横

谢谢友友们耐心观看啦~,若有偏颇,欢迎及时私信指出喔💖💖💖

基础算法持续更新中ing~

  • 40
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 57
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值