【简单数论】欧拉函数、快速幂、扩展欧几里得算法

本文介绍简单数论中的一些内容。包括欧拉函数、快速幂以及扩展欧几里得定理。

1、前置知识

给出一些学习的前置知识,需要掌握以下概念(证明不做赘述):

  1. 同余的概念:当两个 整数 a, b 除以同一个 正整数 p,若得相同 余数 ,则二整数 同余。 记作 a≡b(mod p)。举个例子,5≡7(mod2);6≡-9(mod5)。注意,这里的的余数一定是正数,例如 -9%5=1。
  2. 互质的概念:两个数公约数只有1。
  3. 欧拉函数:1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 φ(N)。规定φ(1)=1。
  4. 欧拉定理:如果a和n互质,则aφ(n)≡1(modn)。
    例如5φ(6)≡1(mod6),即52≡1(mod6)。
  5. 欧拉定理的推论费马小定理:如果a和n互质且n为质数,
    则an-1≡1(modn)。
  6. 裴蜀定理:给定正整数a, b,那么一定存在正整数x, y,使得
    ax+by=gcd(a, b)。且gcd(a, b)是a, b的线性组合能组合出的最小正整数。

2、欧拉函数

1.1、求一个数的欧拉函数

φ(N)的计算有这样一个公式在这里插入图片描述
所以就能给出欧拉函数的代码了:

//输入一个数x,返回其欧拉函数的值
int phi(int x)
{
    int res = x;
    //分解质因数
    for (int i = 2; i <= x / i; i ++ )
    	//分解到一个就代入公式
        if (x % i == 0)
        {
        	//这里是把每一项中的 /i 提出了,因为整数除以整数得不到小数
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    //特判一下最后的质因数
    if (x > 1) res = res / x * (x - 1);
    
    return res;
}

时间复杂度就是分解质因数的时间复杂度,也就是O( n \sqrt n n )。


给出一个简单证明(不要求掌握,重点是记住公式即可):
在这里插入图片描述

将所给公式展开,就可以得到最后那一坨了。


1.2、求1~N中所有数的欧拉函数

如果一个一个求,时间复杂度就是O(n n \sqrt n n ),太慢了。这里用到欧拉筛筛质数的方法,先回顾一下欧拉筛(不懂的同学可以参考:【简单数论】质数和约数):

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

这里给出公式:在这里插入图片描述
那么也就能写出代码了:

//输入n,将1~n的欧拉函数存入phi数组
void get_eulers(int n)
{
    phi[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);
        }
    }
}

时间复杂度自然就变成线性O(N)了。


给出简单证明,不要求掌握:
在这里插入图片描述

3、快速幂

正常求an%p,时间复杂度是O(n)的。但是使用快速幂,可以将时间复杂度降低为O(logn)
快速幂的大致思路为:
在这里插入图片描述
那么就可以给出代码了:

//求出a^b%p
long long qmi(int a, int b, int p)
{
	long long res = 1;
	while (b)
	{
		//a用来记录每一项
		//b&1会得到b的最后一位是0还是1
		if (b & 1) res = res * a % p;
		a = (long long)a * a % p;
		b >>= 1;
	}
	return res;
}

4、扩展欧几里得算法

4.1、扩展欧几里得算法介绍

先回顾一下裴蜀定理:给定正整数a, b,那么一定存在正整数x, y,使得ax+by=gcd(a, b)。且gcd(a, b)是a, b的线性组合能组合出的最小正整数。

扩展欧几里得算法就是求出这样一对x, y(不唯一)的。先给出代码:

//求得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;
}

下面进行推导:
在这里插入图片描述
为了不用扭着记,所以我们在递归的时候直接把x和y扭过来就行了。也就是

exgcd(b, a % b, y, x);

4.2、扩展欧几里得算法应用:求解线性同余方程

原题acwing 线性同余方程
给定 n 组数据 a i a_i ai, b i b_i bi, m i m_i mi,对于每组数求出一个 x i x_i xi,使其满足 a i a_i ai× x i x_i xi b i b_i bi(mod m i m_i mi),如果无解则输出 impossible。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组数据 a i a_i ai, b i b_i bi, m i m_i mi
输出格式
输出共 n 行,每组数据输出一个整数表示一个满足条件的 x i x_i xi,如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
1≤n≤105,
1≤ a i a_i ai, b i b_i bi, m i m_i mi≤2×109
输入样例

2
2 3 6
4 3 5

输出样例

impossible
-3

ax≡b(mod m)也就是(ax-b)\m能得到一个整数,也即ax=my+b,也即ax-my=b,由于y是任意整数,所以合并负号,就是
ax-my=b。可以看出,这就是一个扩展欧几里得算法。它有解的充分必要条件是:b是a和m最大公约数的倍数。所以就能给出代码了:

#include <iostream>

using namespace std;

typedef long long LL;

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;
}

int main()
{
    int n;
    cin >> n;
    
    while (n -- )
    {
        int a, b, m;
        scanf("%d%d%d", &a, &b, &m);
        
        int x, y;
        int d = exgcd(a, m, x, y);
        //如果b不是d的倍数,一定无解
        if (b % d) puts("impossible");
        //因为到最后到落到整数范围内,就%m
        else printf("%lld\n", (LL)b / d * x % m);
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值