一個神奇的算法, 快速求出 A^x=B(mod C),C是素數,求最小的非負x值 若x有解,那麼 0<=x < C, 爲什麼?
由費馬小定理可以得a^(c-1) = 1(mod c)
那麼0~c-1必定是一個循環節(不一定是最小的)。既然是%c,那麼B一定是0到c-1之間的一個數。最壞的條件下,a的c-1以內次方%c的餘數各不相同,那麼在0~C-1時一定存在一個x滿足條件。
那麼不是最壞情況時, 就能找到一個更小的在0~c-1的循環節。BSGS的算法是這樣的:
首先取m=sqrt(c)向上取整。(爲什麼取sqrt(c)?我也不是很懂)
然後先預處理a的0到m次方。
a^x=b ( %c ) 設x=i*m+j; 即: i爲x/m,j爲x%m。 a^(i*m+j)=b; b * (a^(-m))^i =
a^j ( %c )先枚舉j,把右邊存起來(Hash 下一步用二分查找, 或哈希表) 枚舉i,如果左邊的數值曾經存儲過(b * (a^(-m))^i =
a^j),則 x=i*m+j, 得解。- 快速冪, 其實很好理解, 對於求x^y mod p
我們可以把每次的乘積存下來, 最後一起累乘, 即
2^10
= 2^5 * 2^5
那麼每次把y>>=1, 如果y是奇數那麼就把這次分出的答案乘進答案 - xy mod p = z mod p
求最小的x, 其實很簡單, 可以將方程變成ax+by=c
同時除以gcd(a, b), 如果c除不盡那麼無解
否則變成a0x+b0y=c0;->(a0x+b0y=1;x,y乘以c0)
下面給一個例題;
給出下列三種操作
1、給定 y、z、p,計算 y^z mod p 的值;
2、給定 y、z、p,計算滿足 xy ≡z(mod p)的最小非負整數 x;
3、給定 y、z、p,計算滿足 y^x ≡z(mod p)的最小非負整數 x.
三個操作都在上面講過了下面給出代碼
#include <bits/stdc++.h>
using namespace std;
#define hash Hash
#define rep(i, s, t) for(int i = s; i <= t; ++i)
typedef long long ll;
ll p;
ll calc1(ll x, ll y) {
ll res = 1LL;
for(; y; y>>=1) {if(y & 1) res = res*x % p; x = x*x % p;}
return res;
}
ll gcd(ll a, ll b) {
while(b) {
ll t = a;
a = b;
b = t%b;
}
return a;
}
void exgcd(ll a, ll b, ll &x, ll &y) {
if(!b) {
x = 1; y = 0;
}
else {
exgcd(b, a%b, y, x);
y -= a/b*x;
}
}
struct HASH_TABLE {
const ll mod = 100007;
ll h[100007+10][2];
ll find(ll x) {
ll t = x % mod;
while(h[t][0] != x && h[t][0] != -1) t = (t+1) % mod;
return t;
}
void insert(ll x, ll id) {
ll t = find(x);
if(h[t][0] == -1)
h[t][0] = x, h[t][1] = id;
}
ll get(ll x) {
ll t = find(x);
return h[t][1];
}
void BSGS(ll a, ll b, ll p) {
memset(h, -1, sizeof(h));
ll m = ceil(sqrt(p)), t = 1;
rep(i, 0, m-1) {
insert(t, i);
t = t*a%p;
}
ll d=1, x, y;
ll ans = -1;
rep(i, 0, m-1) {
exgcd(d, p, x, y);
x = ((x*b) % p + p)%p;
y = get(x);
//printf("%lld,%lld\n",x,y);
if(y ^ -1) {
ans = i*m+y;
break;
}
d = d*t % p;
}
if(ans == -1) puts("Orz, I cannot find x!");
else printf("%lld\n", ans);
}
}T;
int main() {
#ifndef ONLINE_JUDGE
freopen("calc.in", "r", stdin);
freopen("calc.out", "w", stdout);
#endif
int _, cmd;
scanf("%d%d", &_, &cmd);
while(_--) {
ll y, z, a, b;
scanf("%lld%lld%lld", &y, &z, &p);
if(cmd == 1) cout << calc1(y, z) << endl;
else if(cmd == 2) {
ll Gcd = gcd(y, p);
if(z % Gcd)
puts("Orz, I cannot find x!");
else {
z %= p;
y /= Gcd; p /= Gcd; z /= Gcd;
exgcd(y, p, a, b);
a = (a * z % p + p) % p;
printf("%lld\n", a);
}
}
else {
if(y%p==0) {
if(z==1) printf("0\n"); else puts("Orz, I cannot find x!");
continue;
}
T.BSGS(y, z, p);
}
}
return 0;
}