数论——裴蜀定理、欧几里得算法、扩展欧几里得算法、逆元以及求解

2 篇文章 0 订阅
裴蜀定理

若整数 ab 互质(最大公约数为 1),则存在整数 xy ,使得 ax + by = 1 。

更一般的情况是:设 ab 是不全为零的整数,则存在整数 xy ,使得 ax + by = gcd(a, b) ,其中 gcd(a, b) 表示 a 和 b 的最大公约数。

裴蜀定理有以下几个重要的应用:

求解线性不定方程:例如给定方程 3x + 5y = 7 ,可以利用裴蜀定理判断是否有整数解。

在数论中的证明:帮助证明其他与整数相关的定理和结论。

求解模运算中的问题:比如在某些同余方程的求解中发挥作用。

BZOJ1441——Min

Min - 题目详情 - BZOJ by HydroOJ

#include<iostream>
#include<math.h>
using namespace std;
int gcd(int a, int b) {
	return (b == 0) ? a : gcd(b, a % b);
}
int main() {
    int n, m[10010];
    int ret = 0;
    cin >> n;
    cin >> m[0];
    ret = m[0];
    for(int i=1;i<n;i++){
        cin >> m[i];
        ret = gcd(ret,m[i]);
	}
    cout << abs(ret) << endl;
}

整体框架

  • 包含了必要的输入输出流和数学库的头文件。
  • 在 main 函数中进行主要的操作。

函数 gcd

  • 这是一个用于计算两个数最大公约数的递归函数。
  • 基于欧几里得算法,当 b 为 0 时,返回 a 作为最大公约数;否则,通过递归调用计算 b 和 a % b 的最大公约数。

main 函数

  • 首先读取一个整数 n ,表示接下来要输入的数的个数。
  • 读入第一个数 m[0] ,并将其初始化为 ret 。
  • 然后通过一个循环,依次读入后续的数 m[i] ,并不断更新 ret 为它与当前 m[i] 的最大公约数。
  • 最后输出计算得到的最大公约数的绝对值。
欧几里得算法(辗转相除法)

a|b表示a整除b;如3|6;a<b

a|\b表示a不整除b;如5|\7;

|可以表示对整数的划分;

int gcd(int a,int b){
return (b==0)?a:gcd(b,a%b);
}
#include<iostream>
using namespace std;
int gcd(int a, int b) {
	return (b == 0) ? a : gcd(b, a % b);
}
int main() {
	int a, b;
	while(cin >> a >> b){
	cout << gcd(a, b) << endl;
}
}
扩展欧几里得算法

int extend_gcd(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    } else {
        int ret = extend_gcd(b, a % b, y, x);
        y -= x * (a / b);
        return ret;
    }
}
#include<iostream>
using namespace std;
int extend_gcd(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    else {
        int ret = extend_gcd(b, a % b, y, x);
        y -= x * (a / b);
        return ret;
    }
}
int main() {
    int a, b, x, y;
    while (cin >> a >> b >> x >> y) {
        cout << extend_gcd(a, b, x, y) << endl;
        return 0;
    }
}
逆元

  1. 在密码学中的应用:例如在 RSA 加密算法中,逆元的计算是关键步骤之一。

  2. 用于优化计算:在涉及模运算的复杂计算中,使用逆元可以简化计算过程,提高效率。

计算逆元的方法通常包括扩展欧几里得算法、费马小定理等。

扩展欧几里得算法求逆元

int inverse(int a, int b) {
    int x, y;
    extend_gcd(a, b, x, y);
    return x;
}
#include <iostream>

// 扩展欧几里得算法函数
int extendedEuclidean(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }

    int x1, y1;
    int gcd = extendedEuclidean(b, a % b, x1, y1);

    x = y1;
    y = x1 - (a / b) * y1;

    return gcd;
}

// 计算逆元的函数
int modInverse(int a, int m) {
    int x, y;
    int gcd = extendedEuclidean(a, m, x, y);
    if (gcd!= 1) {
        std::cout << "Inverse doesn't exist" << std::endl;
        return -1;
    } else {
        return (x % m + m) % m;
    }
}

int main() {
    int a = 3, m = 11;
    int inv = modInverse(a, m);
    if (inv!= -1) {
        std::cout << "Inverse of " << a << " mod " << m << " is " << inv << std::endl;
    }

    return 0;
}
  1. extendedEuclidean 函数:

    • 这个函数使用递归的方式实现扩展欧几里得算法。
    • 如果 b 为 0 ,则直接设置 x 为 1 ,y 为 0 ,并返回 a ,此时 a 就是最大公约数。
    • 否则,通过递归调用自身计算 b 和 a % b 的最大公约数以及对应的解 x1 和 y1 。
    • 然后根据递归返回的结果计算当前层的 x 和 y 。
  2. modInverse 函数:

    • 首先调用 extendedEuclidean 函数计算 a 和 m 的最大公约数以及对应的解 x 和 y 。
    • 如果最大公约数不为 1 ,说明逆元不存在,输出提示信息并返回 -1 。
    • 如果最大公约数为 1 ,则通过计算 (x % m + m) % m 得到逆元并返回。这里加上 m 再取模是为了确保结果为正数。
  3. main 函数:

    • 定义了整数 a 和 m 。
    • 调用 modInverse 函数计算 a 模 m 的逆元,并根据返回结果进行输出。
欧拉定理求逆元

int power_mod(int a, int b, int n) {
    int ret = 1;
    while (b) {
        if (b & 1)
            ret = (long long)ret * a % n;
        a = (long long)a * a % n;
        b >>= 1;
    }
    return ret;
}
#include<iostream>
using namespace std;
int power_mod(int a, int b, int n) {
    int ret = 1;
    while (b) {
        if (b & 1)
            ret = (long long)ret * a % n;
        a = (long long)a * a % n;
        b >>= 1;
    }
    return ret;
}

int main() {
    int a, b,n;
    while (cin >> a >> b >>n) {
        cout << power_mod(a, b, n) << endl;
    }
        return 0;
    }
线性求逆元:递推法
for (inverse[1] = 1, i = 2; i <= n; ++i)
    inverse[i] = inverse[p % i] * (p - p / i) % p;
线性求逆元:倒推法

#include <iostream>

const int MOD = 1000000007;
const int MAXN = 100005;

int inv[MAXN];

void calculateInverse(int n) {
    inv[1] = 1;
    for (int i = 2; i <= n; i++) {
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
}

int main() {
    int n = 5;
    calculateInverse(n);
    for (int i = 1; i <= n; i++) {
        std::cout << "Inverse of " << i << " is: " << inv[i] << std::endl;
    }
    return 0;
}
  • 首先定义一个常量 MOD 表示模数,一个常量 MAXN 表示可能的最大数的范围,以及一个数组 inv 来存储每个数的逆元。

  • 在 calculateInverse 函数中,先初始化 inv[1] = 1 ,因为 1 的逆元就是 1 。

  • 对于大于 1 的数 i ,通过公式 inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD 来计算逆元。这个公式的推导基于数论的知识。

  • 在 main 函数中,指定要计算逆元的数的范围 n ,调用 calculateInverse 函数进行计算,然后输出每个数的逆元。

POJ1845——Sumdiv

1845 -- Sumdiv

BZOJ2186——沙拉公主的困惑

[Sdoi2008]沙拉公主的困惑 - 题目详情 - BZOJ by HydroOJ
#include <cstdio>
#include <algorithm>

using namespace std;

inline void exgcd (int a, int b, int &x, int &y)
{
	if(b == 0) { x = 1; y = 0; return ; }
	else { exgcd(b, a%b, x, y); int tmp = x; x = y; y = tmp - a / b * y; }
}

inline int getinv (int a, int p)
{
	int x, y;
	exgcd(a, p, x, y);
	return (x%p+p)%p;
}

bool np[10000005];
int l, p[664580];
long long fac[10000005];
long long inv[664580];
long long mul[664580];

int main (void)
{
	int t, mod, n, m;
	scanf("%d %d", &t, &mod);
	fac[1] = 1; np[0] = np[1] = 1;
	inv[0] = 1; mul[0] = 1;
	for(register int i=2; i<=10000000; ++i)
	{
		fac[i] = fac[i-1] * i % mod;
		if(!np[i])
		{
			p[++l] = i;
			inv[l] = inv[l-1] * getinv(p[l], mod) % mod;
			mul[l] = mul[l-1] * (p[l] - 1) % mod;
		}
		for(register int j=1; j<=l; ++j)
		{
			if(i*p[j]>10000000) break;
			np[i*p[j]] = 1;
			if(i%p[j]==0) break;
		}
	}
	while(t--)
	{
		scanf("%d %d", &n, &m);
		register int mid = upper_bound(p+1,p+l+1,m)-p-1;
		printf("%lld\n", fac[n] * inv[mid] % mod * mul[mid] % mod);
	}
	return 0;
}

整体框架

  • 首先包含了必要的头文件,并定义了一些函数和变量。
  • 在 main 函数中处理输入的测试用例,进行相关计算并输出结果。

函数 exgcd

  • 这是扩展欧几里得算法,用于求解线性方程 ax + by = gcd(a, b) 中的 x 和 y
  • 通过递归的方式,当 b 为 0 时直接确定 x 和 y 的值,否则在递归后交换 x 和 y 的值,并进行相应的计算来更新 y

函数 getinv:利用 exgcd 函数来计算 a 在模 p 意义下的逆元。

预处理部分

  • 初始化一些数组和变量,如 np 数组标记是否为合数,p 数组存储质数,fac 数组存储阶乘值,inv 数组存储质数逆元的乘积,mul 数组存储质数减 1 的乘积。
  • 通过两层循环来筛选出质数,并计算相关的值。

测试用例处理部分

  • 每次读入 n 和 m
  • 通过二分查找找到小于等于 m 的最大质数的索引 mid
  • 最终计算并输出 fac[n] * inv[mid] % mod * mul[mid] % mod 的结果。
  • 26
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
扩展欧几里得算法求解一元一次不定方程 ax + by = gcd(a,b) 的一种方法,其中 a 和 b 是整数,gcd(a,b) 是它们的最大公约数,x 和 y 是整数解。逆元是指在模运算下,一个数的乘法逆元是指与它相乘后模运算得到 1 的数。在数论中,常常需要求一个数在模意义下的逆元,即一个数 k 满足 (k * x) % m = 1,其中 m 是模数。 下面是扩展欧几里得算法逆元的 C 语言实现: ```c #include <stdio.h> // 扩展欧几里得算法 int exgcd(int a, int b, int *x, int *y) { if (b == 0) { *x = 1; *y = 0; return a; } int gcd = exgcd(b, a % b, y, x); *y -= a / b * (*x); return gcd; } // 求逆元 int modinv(int a, int m) { int x, y; int gcd = exgcd(a, m, &x, &y); if (gcd != 1) { return -1; // a 和 m 不互质,不存在逆元 } else { return (x % m + m) % m; // 转化为正整数 } } int main() { int a = 3, m = 11; int inv = modinv(a, m); if (inv == -1) { printf("%d 在模 %d 意义下不存在逆元\n", a, m); } else { printf("%d 在模 %d 意义下的逆元是 %d\n", a, m, inv); } return 0; } ``` 这个程序中,exgcd 函数通过递归实现扩展欧几里得算法,返回 a 和 b 的最大公约数,并且求出 x 和 y 的值。在 modinv 函数中,我们调用 exgcd 函数求出 a 和 m 的最大公约数,并且判断 a 和 m 是否互质,如果不互质则不存在逆元。否则,根据扩展欧几里得算法的结果,求出 x 的值作为 a 在模 m 意义下的逆元。注意,由于 x 可能是负数,所以要将其转化为正整数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

筱姌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值