洛谷 P1082 [NOIP2012 提高组] 同余方程

洛谷 P1082 [NOIP2012 提高组] 同余方程

刷一刷数论的题,这才知道有扩展欧几里得算法这个东西(扩欧)。虽然还不知道欧几里得算法是什么,就一步步来看。

欧几里得算法,在《整除与剩余》板块找到了。

求两个数a,b的最大公约数。(哦豁,原来欧几里得算法就是求最大公约数的算法,幻想的太高大上了)。

接口:
int gcd(int a,int b);
复杂度 O(logN),N和a,b同阶
输入:a,b 两个整数
输出:a,b的最大公约数

代码:

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

从欧几里得算法还引出一个叫做贝祖定理的东西,看起来就像欧几里得算法的前身,不过通过计算机更容易实现a%b的操作。贝祖定理是这样说的:

gcd(a,b) = gcd(b,a-b),其中a≥b。如此不断迭代,直到b=0,a就是原来数对的最大公约数。把a减b减到不能减为止,在计算机上直接表示为 a mod b就好。

看完贝祖定理的描述,想起了开始学习C语言时使用的辗转相除法:

代码:

//余数rem,数对a, b
int gcd(int a,int b){
	while(b>0){
		rem = a % b;
		a = b;
		b = rem;
	}
	return a;
}

从辗转相除和贝祖定理,到欧几里得算法,形式变得越来越简单,性能变得越来越强大(竖大拇指)。

记小本本了:

//欧几里得算法:求a,b的最大公约数
int gcd(int a,int b){
	return b == 0? a :gcd(b, a % b);
}

写这么多,还没说题呢。

此题要使用扩展欧几里得算法,???好像,好像还没有说扩展欧几里得算法。。。继续说叭。

扩展欧几里得算法

用于求A,B的最大公约数,且求出X,Y满足AX+BY=GCD(A,B)
看得出来,多了点东西,嗯,有意思:

AX+BY=GCD(A,B)
让我们看看这个东西怎么理解。

刚刚说过求最大公约数gcd:

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

我们知道,A和A是两个非负整数,每次调用函数A用B替换,B用A%B替换,有两种情况。

第一种情况,B=0了,B==0,return返回值就是A了。
左边成了AX,右边GCD(A,B)就是A。

显而易见,X=1,Y=0(此时Y是任何数都没有影响了,因为B是0,0乘任何数都是0,代码中,当B=0时,更改Y的值结果不变)

第二种情况,B>0,我们已经讲了GCD(A, B) = GCD(B,A mod B)
A可以替换为B,B可以替换为A%B,然后把前面也替换一下看看。

BX’ + (A mod B)Y’ = GCD(B, A mod B) = GCD(A, B)

其中X’是变换后的原来X位置的值,Y’是变换后的原来Y位置的值。

结合一点点数学知识,和C语言的向下取整,把A mod B 变一下形式。

A%B = (A - A/B × B)Y’ ,展开:

AY’ + BX’ - (A/B) × BY’ = GCD(A, B),提取一下B:

AY’ + B(X’ - A/B × Y’) = GCD(A, B)

与原式子对比一下:AX+BY=GCD(A,B)

能得出来:

AY’ + B(X’ - A/B × Y’) = AX+BY,成!

X = Y’, Y = X’ - A/B × Y’,爽!

接下来代码造出来:

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

接口:
void exgcd(int a,int b);
复杂度:O(logN), N与a,b同阶。
输入:a,b 两个整数。
输出:x就为满足条件 ax ≡ 1(mod b)的a的乘数。

如此,扩展欧几里得算法:

继续记小本本:

//扩展欧几里得算法
void exgcd(int a,int b){
	if(b==0){
		x=1;
		y=0;
		return;
	}	
	exgcd(b, a%b);
	k = x;
	x = y;
	y = k - x*(a/b);
	return;
}

诶!ax ≡ 1(mod b)这是个啥。继续下去。

这个柿子(式子)的意思是a,b已知,求x,满足(a*x)%b = 1

x 有个自己的名字 乘法逆元。

(终于离题目近了一点)

继续套娃:乘法逆元又是个啥:

离散数学上有概念:存在群G,中有元素a,e为单位元。
满足 a’ × a = a × a’ = e,a’为逆元。
(其中单位元e是:a×e = a)在实数中任何数乘1得他本身,所以1是单位元。)

看题吧,x的同余方程 ax≡ 1 (mod b),求最小整数解。

这不是求乘法逆元吗?

安排:

#include<iostream>

typedef long long ll;
using namespace std;

ll x ,y;
//扩展欧几里得算法求乘法逆元
void exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	ll tx = x;
	x = y;
	y = tx - a/b * y;
}

int main()
{
	ll a,b;
	cin >> a >> b;
	exgcd(a,b);
	x = (x%b+b)%b;
	cout << x;
	return 0;
}

end!

上一篇:算法刷题【洛谷P1080 & NOIP2012 提高组】国王游戏(附sort cmp函数使用警告)


下一篇:手把手教你学node之搭建node.js开发环境