Step1 Problem:
a^x ≡ b(mod m).
给你 a, b, m, 求 x.
数据范围:
1<=T<=500, 0 <= a, b < m <= 1e9.
Step2 Ideas:
学习算法博客
说说自己的理解:
a^x ≡ b(mod m).
a 和 m 互质:
根据欧拉定理:如果 gcd(a, m) == 1, a^phi(m) ≡ 1(mod m).
所以 a 和 m 互质,a^x mod m 隔 phi(m) 次一定存在周期。
a^x ≡ b(mod m).
x = b * s + r:需要用逆元
由于 phi(m) < m, 所以我们令 s = sqrt(m), 枚举 x = [0, s) 求出 a^x 用 map 存起来。
当 xi = [i * s+1, (i+1) * s) 时:xi = i * s + x, a^xi ≡ b(mod m) -> a^x ≡ b * a^(-i * s)(mod m).
此时:用 map 判断 b * a^(-i * s) 是否存在,如果存在则有解。
直到 xb = [b * s+1, (b+1) * s) 为止。
x = b * s - r:不需要用逆元
由于代码采用是不用逆元的方法,所以直接看代码即可。
a 和 m 不互质:
我们需要把方程转换成,a 和 m 互质。
a^x ≡ b mod m -> a^x + y*m = b.
由裴蜀定理,g = gcd(a, m) 不整除 b 那么无解返回 -1.
否则:a/g * a^(x-1) + m/g * y = b/g.
模方程:a/g * a^(x-1) ≡ b/g (mod m/g).
令 m1 = m/g, b1 = b/g * (a/g)^(-1),得到新方程:
a^x1 ≡ b1(mod m1).
可知:x = x1+1.
由于 a 是不变的,不断重复上述操作,直到 a 和 m1 互质。
如果 b1 = 1, 此时存在解 x1 = 0. 在求出 x 即可。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll log_ab(ll a, ll b, ll MOD)
{
a = a%MOD; b = b%MOD;
if(b == 1) return 0;
int cnt = 0;
ll t = 1;
for(ll g = __gcd(a, MOD); g != 1; g = __gcd(a, MOD))
{
if(b%g) return -1;//由裴蜀定理,可知无解
MOD /= g, b /= g;
t = t * a / g % MOD;
cnt++;//记录 x -> x1 经过了几次。
if(t == b) return cnt;//b1 = 1.
}
unordered_map<ll, ll> mp;
int m = ceil(sqrt(1.0 * MOD));
ll e = 1;
for(int i = 0; i < m; i++) {//将 a^i 存起来,i = [0, sqrt(m)),如果不用逆元需要将 b*a^i 存起来。
mp[e*b%MOD] = i;
e = e * a % MOD;
}
// e = a^(sqrt(m))
ll nw = t;
for(int i = 1; i <= m + 1; i++) {
nw = e * nw % MOD;
if(mp.count(nw)) {
return i * m - mp[nw] + cnt;
}
}
return -1;
}
int main()
{
int T, a, b, m;
scanf("%d", &T);
while(T--)
{
scanf("%d %d %d", &a, &b, &m);
ll x = log_ab(a, b, m);
if(x == -1) ;
else printf("%lld\n", x);
}
return 0;
}