所谓的欧几里得算法就是辗转相除法,作用是求两个数的最大公约数
它其实用到的是一个定理:
g c d ( a , b ) = g c d ( b , a m o d b ) ; gcd(a,b) = gcd(b, a\ mod\ b); gcd(a,b)=gcd(b,a mod b);
《算法导论》上有上式严密的证明,大体就是证明
g c d ( a , b ) ∣ g c d ( b , a m o d b ) & & g c d ( b , a m o d b ) ∣ g c d ( a , b ) gcd(a,b) | gcd(b, a\ mod\ b) \&\& gcd(b, a\ mod\ b)|gcd(a,b) gcd(a,b)∣gcd(b,a mod b)&&gcd(b,a mod b)∣gcd(a,b)
然后根据推论a|b&&b|a 可以说明 |a| = |b| 证明的。
代码:
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
这个算法其实是解类似于ax+by=k*gcd(a,b)这个公式的通解的,其实仅仅用拓欧只能求得一个特解,设特解 x 0 , y 0 x_0 ,y_0 x0,y0我们可以用
x = x 0 + b k t / g c d ( a , b ) y = y 0 − a k t / g c d ( a , b ) x = x_0+bkt/gcd(a, b) \\ y = y_0-akt/gcd(a, b) x=x0+bkt/gcd(a,b)y=y0−akt/gcd(a,b)
(t是整数,使得x,y在指定范围)求出在一定范围内的所有解。
知道了拓欧在求不定方程的作用,那如何求那个特解呢?
我们可以把求特解的过程当成辗转相除法的逆运算,本质是个递归
a x 1 + b y 1 = g c d ( a , b ) b x 2 + ( a m o d b ) y 2 = g c d ( a , b ) ax_1+by_1=gcd(a,b) \\ bx_2+(a\ mod \ b)y_2 = gcd(a,b) ax1+by1=gcd(a,b)bx2+(a mod b)y2=gcd(a,b)
这里可以在进一步用x2,y2推x1,y1
a x 1 + b y 1 = b x 2 + ( a m o d b ) y 2 a x 1 + b y 1 = b x 2 + ( a − ⌊ a b ⌋ b ) y 2 a x 1 + b y 1 = a y 2 + ( b x 2 − ⌊ a b ⌋ b y 2 ) 所 以 : x 1 = y 2 y 1 = x 2 − ⌊ a b ⌋ ∗ y 2 ax_1+by_1=bx_2+(a\ mod\ b)y_2\\ ax_1+by_1=bx_2+(a - \lfloor\frac{a}{b}\rfloor b)y_2\\ ax_1+by_1=ay_2+(bx_2 - \lfloor\frac{a}{b}\rfloor by_2)\\ 所以:\\ x_1 = y_2\\ y_1 = x_2-\lfloor\frac{a}{b}\rfloor*y_2 ax1+by1=bx2+(a mod b)y2ax1+by1=bx2+(a−⌊ba⌋b)y2ax1+by1=ay2+(bx2−⌊ba⌋by2)所以:x1=y2y1=x2−⌊ba⌋∗y2
递归到尽头是b=0然后就可以令x=1,y=0,然后返回a就是gcd的值,有了理论基础代码就不难写出了。
int gcdEx(int a, int b, int & x, int & y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
int ret = gcdEx(b, a % b, y, x);
y -= (a/b)*x;
return ret;
}
UVA - 12169
这道题暴力可以莽枚举a和b就行,但比较好的方法是根据递推式,只枚举a然后用拓欧推b,然后带入检验。
代码:
#include
using namespace std;
typedef long long ll;
ll gcdEx(ll a, ll b, ll & x, ll & y) {
if(!b) {
x = 1;
y = 0;
return a;
}
else {
ll ret = gcdEx(b, a % b, y, x);
y -= (a/b)*x;
return ret;
}
}
const ll mod = 10001;
ll x[mod+10];
int main()
{
// freopen("/Users/maoxiangsun/MyRepertory/acm/i.txt", "r", stdin);
ll T;
scanf("%lld", &T);
for(ll i = 1; i < 2*T; i+=2) {
scanf("%d", &x[i]);
}
for(ll a = 0; ; a++) {
ll c = x[3]-a*a*x[1];
ll b,y;
// cout << "-0";
ll g = gcdEx(a+1, mod, b, y);
if(c % g) continue;
b = b * c / g;
bool flag = true;
for(ll i = 2; i <= 2*T; i++) {
if(i & 1) {
if(x[i] != (x[i-1]*a + b)%mod) {
flag = false;
break;
}
}
else x[i] = (x[i-1] * a + b)%mod;
}
if(flag) break;
}
for(ll i = 2; i <= 2*T; i+=2) {
printf("%lld\n", x[i]);
}
return 0;
}