歐幾里得演算法&&拓展歐幾里得演算法
參考部落格http://www.cnblogs.com/void/archive/2011/04/18/2020357.html
參考部落格http://blog.csdn.net/zhjchengfeng5/article/details/7786595
參考部落格http://blog.csdn.net/loi_dqs/article/details/49488851
歐幾里得演算法
int gcd(int a, int b)
{
return b == 0? a : gcd(b,a % b);
}
拓展歐幾里得演算法
注意:ans不能刪去,因為這是從後往前遞迴的。即先一直往下呼叫,一直呼叫到結束,然後此刻得到x,和y的值然後一層一層的回溯,一層一層的得到上一層的值。int e_gcd(int a, int b,int &x,int &y) { if(b == 0) { x = 1; y = 0; return a; } int ans = e_gcd(b,a % b, x, y); int tem = x; x = y; y = tem - a/b*y; return ans; }
拓展歐幾里得演算法演算法跟歐幾里得演算法區別在於
拓展能夠求出每一步a,b對應方程ax + by = gcd(a,b)對應的x,y
假設當前我們要處理的是求出 a 和 b的最大公約數,並求出 x 和 y 使得 a*x + b*y= gcd ,而我們已經求出了下一個狀態:b 和 a%b 的最大公約數,並且求出了一組x1 和y1 使得: b*x1 + (a%b)*y1 = gcd , 那麼這兩個相鄰的狀態之間是否存在一種關係呢?
我們知道: a%b = a - (a/b)*b(這裡的 “/” 指的是整除,例如 5/2=2 , 1/3=0),那麼,我們可以進一步得到:
gcd = b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1
= a*y1 + b*(x1 – a/b*y1)
對比之前我們的狀態:求一組 x 和 y 使得:a*x + b*y = gcd ,是否發現了什麼?
這裡:
x = y1
y = x1 – a/b*y1
拓展歐幾里得應用
(1)求解不定方程;
(2)求解模線性方程(線性同餘方程);
(3)求解模的逆元;
(1)使用擴充套件歐幾里德演算法解決不定方程的辦法:
青蛙的約會
兩隻青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面為止。可是它們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們覺得只要一直朝著某個方向跳下去,總能碰到對方的。但是除非這兩隻青蛙在同一時間跳到同一點上,不然是永遠都不可能碰面的。為了幫助這兩隻樂觀的青蛙,你被要求寫一個程式來判斷這兩隻青蛙是否能夠碰面,會在什麼時候碰面。
我們把這兩隻青蛙分別叫做青蛙A和青蛙B,並且規定緯度線上東經0度處為原點,由東往西為正方向,單位長度1米,這樣我們就得到了一條首尾相接的數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。現在要你求出它們跳了幾次以後才會碰面。
1 2 3 4 5Sample Output
4
思路:先用exgcd求ax0 + by0 = gcd(a,b)對應的x0,y0
令 d = gcd(a,b)
然後x,y分別乘以c/d得到的x1,y1滿足ax1 + by1 = c。
x1,y1就是一組解,其他的解可表示為
x = x1 - b/d*t;
y = x2 + a/d*t;
例如求x的最小正整數解
long long t = b/d;
if(x >= 0)
x = x % t;
else
x = x % t + t;
ac程式碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
long long exgcd(long long a, long long b, long long &x, long long &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
long long ans = exgcd( b, a % b, x, y);
long long tem = x;
x = y;
y = tem - a/b*y;
return ans;
}
int main()
{
long long x, y, m, n, l;
scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
long long a, b, c;
a = n - m;//不用管其正負
b = l;
c = x - y;
long long d = exgcd(a,b,x,y);
if(c % d != 0)
printf("Impossible\n");
else
{
x *= c/d;
long long t = b/d;
if(x >= 0)
x = x % t;
else
x = x % t + t;
printf("%lld\n", x);
}
return 0;
}
注意,此資料太大,一定使用long long 否則將會wrong answer
(2)可以轉化為(1)
(3)
滿足 a * k ≡ 1 (mod p) 的k 叫做 a關於p的乘法逆元。另一種表達方法是 k ≡ a-1 (mod p)
逆元在密碼學中有廣泛應用,AES密碼體系的位元組替代就是運用了逆元。(不知道說的smg)
應用:
我們知道(a+b)%p=(a%p+b%p)%p
(a*b)%p=(a%p)*(b%p)%p
而求(a/b)%p時,可能會因為a是一個很大的數,不能直接算出來,卻又不能(a/b)%p=(a%p/b%p)%p。
但是可以通過 k ≡ b-1 (mod p)
a / b
= a * b-1 (mod p )
= (a mod p ) * (b-1 mod p ) mod p
= (a mod p ) * (kmod p ) mod p
= a * k mod p
轉換為a*k % p 的問題,然後a是一個加減乘的式子,就可以用上面兩個取餘公式了。
這裡,我們稱 x 是 a 關於 m 的乘法逆元
這怎麼求?可以等價於這樣的表示式: a*x + m*y = 1
看出什麼來了嗎?沒錯,當gcd(a , m) != 1 的時候是沒有解的這也是 a*x + b*y = c 有解的充要條件: c % gcd(a , b) == 0
轉化為(1)
int cal(int a, int m)
{
int x, y;
int d = exgcd(a,m,x,y);
if(1%d != 0)
return -1;
x *= 1/d;
m = abs(m);
int ans = x % m;
if(ans <= 0)
ans += m;
return ans;
}
Modular Inverse
The modular modular multiplicative inverse of an integer a modulo
m is an integer x such that a-1≡x (modm)
. This is equivalent to
ax≡1 (modm)
.
There are multiple test cases. The first line of input is an integer T ≈ 2000 indicating the number of test cases.
Each test case contains two integers 0 < a ≤ 1000 and 0 < m ≤ 1000.
<h4< dd="">OutputFor each test case, output the smallest positive x. If such x doesn't exist, output "Not Exist".
<h4< dd="">Sample Input3 3 11 4 12 5 13<h4< dd="">Sample Output
4 Not Exist 8
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1;
y = 0;
return a;
}
int ans = exgcd(b, a%b, x, y);
int tem = x;
x = y;
y = tem - a/b*y;
return ans;
}
int cal(int a, int m)
{
int x, y;
int d = exgcd(a,m,x,y);
if(1%d != 0)
return -1;
x *= 1/d;
m = abs(m);
int ans = x % m;
if(ans <= 0)
ans += m;
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
int a, m;
scanf("%d %d", &a, &m);
int ans = cal(a,m);
if(ans == -1)
printf("Not Exist\n");
else
printf("%d\n", ans);
}
return 0;
}