NOIP2012Day2T1同余方程解题报告以及扩展欧几里得讲解

题目描述

求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解。

输入输出格式
输入格式:

输入只有一行,包含两个正整数 a, b,用一个空格隔开。

输出格式:

输出只有一行,包含一个正整数 x0,即最小正整数解。输入数据保证一定有解。

输入输出样例

输入样例#1:
3 10

输出样例#1:
7

说明

【数据范围】

对于 40%的数据,2 ≤b≤ 1,000;

对于 60%的数据,2 ≤b≤ 50,000,000;

对于 100%的数据,2 ≤a, b≤ 2,000,000,000。

NOIP 2012 提高组 第二天 第一题

这是一道数论的题目,首先先说下同余的概念同余的形式就是ax≡kmod(b)他的形式就可以转变为–>ax mod b = k这样子的形式。用集合论的语言,严格地来说就是:

对于整数集的任意一个子集Z,对于任意一个属于Z的元素n,n都除以m,得到的余数可以为0,1,2,…m-1,共m种。我们就以余数的大小作为标准,将Z分为m个互不相交的m个子集Z0,Z1,Z2,…Zm-1。

对于Zi的任意两个元素a,b,都关于m同余。记为

a≡b(mod m)。
我们解同余方程可以用到扩展欧几里得
大概的意思就是如下

int E_gcd(int a,int b,int &x,int &y){
    int ret,temp;
    if(!b){
        x=1;y=0;
        return a;
    }
    ret=E_gcd(b,a%b,x,y);
    temp=x;
    x=y;
    y=temp-a/b*y;
    return ret;
}

因为这里我们要对x,y本身进行修改来实现算法本身,所以在对函数本身定义的时候要加上取地址符来时限对x,y值的修改。
那么明白了扩展欧几里得算法本身之后代码本身就很好写了

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int a,b,x,y;

inline int gcd(int a,int b,int &x,int &y){
    if(!b){
        x=1,y=0;
        return a;
    }
    int r=gcd(b,a%b,x,y);
    int temp=x;
    x=y;
    y=temp-a/b*y;
    return r;
}

int main()
{
    scanf("%d%d",&a,&b);
    gcd(a,b,x,y);
    printf("%d",(x+b)%b);
turn 
0;
}

下面我们来说一下扩展欧几里得

概述

欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:

gcd函数就是用来求(a,b)的最大公约数的。

gcd函数的基本性质:

gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)

公式表述

gcd(a,b)=gcd(b,a mod b)

证明:a可以表示成a = kb + r,则r = a mod b

假设d是a,b的一个公约数,则有

d|a, d|b,而r = a – kb,因此d|r

因此d是(b,a mod b)的公约数

假设d 是(b,a mod b)的公约数,则

d | b , d |r ,但是a = kb +r

因此d也是(a,b)的公约数

因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证
语言实现

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

扩展算法

对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然
存在整数对 x,y ,使得 gcd(a,b)=ax+by。
语言实现

int gcd(int a,int b,int &x,int &y){
if (b==0){
x=1,y=0;
return a;
}
int q=gcd(b,a%b,y,x);
y-=a/b*x;
return q;
}

求解 x,y的方法的理解

设 a>b。

1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;

2,a>b>0 时

设 ax1+ by1= gcd(a,b);

bx2+ (a mod b)y2= gcd(b,a mod b);

根据朴素的欧几里德原理有 gcd(a,b) = gcd(b,a mod b);

则:ax1+ by1= bx2+ (a mod b)y2;

即:ax1+ by1= bx2+ (a – [a / b] * b)y2=ay2+ bx2- [a / b] * by2;

也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);

根据恒等定理得:x1=y2; y1=x2- [a / b] *y2;

这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。

扩展欧几里德算法

扩展欧几里德算法是用来在已知a, b求解一组x,y使得ax+by = Gcd(a, b) =d(解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。实现:

int exGcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exGcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*y;
return r;
}
把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。

可以这样思考:

对于a’=b,b’=a%b 而言,我们求得 x, y使得 a’x+b’y=Gcd(a’,b’)

由于b’=a%b=a-a/b*b (注:这里的/是程序设计语言中的除法)

那么可以得到:

a’x+b’y=Gcd(a’,b’) ===>

bx+(a – a / b * b)y = Gcd(a’, b’) = Gcd(a, b) ===>

ay +b(x – a / b*y) = Gcd(a, b)

因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)

使用扩展欧几里德算法解决不定方程的办法

对于不定整数方程pa+qb=c,若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。

有种较为不严谨的方法证明,不过至少弥补了一点空白,望某些数论大师补充修改:

由于我们知道,存在一组x与y使得a*x+b*y=gcd(a,b)。

将等式两边同时乘以整数k,即a*x*k+b*y*k=gcd(a,b)*k。如果c mod gcd(a,b)=f,则0<=f

点赞