题意:
求模方程 a^x = b(mod n)是否存在解x,如果存在,输出最小的x。
解题思路:
首先考虑简单的情况,求a^x = b(mod n),如果a,n互质,由欧拉定理 a^( phi(n) ) = 1 (mod n)可知,当x>phi(n)后a^x就开始循环了。所以只需要检查前phi(n)项是否有解,因为phi(n) <= n,为了方便,统一设定检查范围为0到n。然后我们先检查前m项 (令m = sqrt(n) ),保存所有a^i mod n的值 (0 <= i < m),接下来对于a^(m),a^(m+1)...a^(2m+1)就不用枚举来检查了,如果他们当中有解,则相当于存在a^i* a^m = b(mod n)。两边左乘a^(-m)得 a^i = ( b*a^(-m) ) (mod n),所以只需要判断有没有a^i = b*a^(-m) 。
如果a,n不互质的话,直接这样子做就不行了,既然我们有了a,n互质的做法,那就要想办法使得a,n互质。
所以每次都要把a,n的gcd约去,同时b也要约去gcd,如果b不能约去gcd说明无解。这样假设需要cnt个a和n约去才能使得a,n互质,这cnt个a约后得到的设为now,结果就是求now*a^x = b (mod n),也就转化成a,n互质的情况了,这样就很简单了~贴个代码 (ps:貌似好久没写博客了)
#include <stdio.h>
#include <math.h>
#include <map>
using namespace std;
#define LL __int64
int exgcd(int a, int b, int &x, int &y) {
if(!b) {
x = 1; y = 0;
return a;
}
int ret = exgcd(b, a%b, y, x);
y -= a/b*x;
return ret;
}
int gcd(int a, int b) {
return b ? gcd(b, a%b) : a;
}
// 求逆元
int inv(int a, int n) {
int x, y;
int d = exgcd(a, n, x, y);
if(x < 0) x += n;
return x;
}
int mul_mod(LL a, LL b, int n) {
return a * b % n;
}
int pow_mod(int x, int n, int m) {
int ret = 1;
while(n) {
if(n&1) ret = (LL)ret*x%m;
x = (LL)x*x%m;
n /= 2;
}
return ret;
}
//求解a^x = b (mod n) a, n互质
int log_mod(int a, int b, int n) {
int m = sqrt(n+0.5);
int v = inv(pow_mod(a, m, n), n);
map<int,int> x;
x[1] = 0;
int e = 1;
for(int i = 1;i < m; i++) {
e = mul_mod(e, a, n);
if(!x.count(e)) x[e] = i;
}
for(int i = 0;i < m; i++) {
if(x.count(b)) return i*m + x[b];
b = mul_mod(b, v, n);
}
return -1;
}
int solve(int a, int p, int b) {
int cnt = 0;
int now = 1;
while(true) {
// 结果已经等于b的情况
if(now%p == b) return cnt;
int d = gcd(a, p);
if(d == 1) {
int v = inv(now, p);
int ret = log_mod(a, (LL)b*v%p, p);
if(ret == -1) return -1;
return ret + cnt;
}
if(b % d) return -1;
b /= d;
p /= d;
now = (LL)now*(a/d)%p;
cnt++;
}
}
int main() {
int a, p, b;
while(scanf("%d%d%d", &a, &p, &b) != -1) {
if(b >= p) {
puts("Orz,I can’t find D!"); continue;
}
int ans = solve(a, p, b);
if(ans == -1) puts("Orz,I can’t find D!");
else printf("%d\n", ans);
}
return 0;
}