对于问题 b ^ x == N % mod 的问题,知道b,N,mod,求最小指数 x;
1.对于mod为素数的时候用朴素baby_step_giant_step算法,算法过程如下:
思路: 令x = i * m + j,m为ceil(sqrt(double(mod)));
那么的话等式为 b ^ j == N * b ^ ( - m * i) % mod,要求x,那么就是把满足这个方程的 i , j 求出来,i,j的范围为m = ceil(sqrt(double(mod))),在范围内对左边的打哈希表,这是baby_step操作;然后对右边进行i递增,使得b是以-m的指数进行递增,所以要先求出b%mod的逆元,可以得出(b ^(-m));进行操作的时候如果满足这个等式的话直接说明i,j找到了,然后 x = i * m + j;
复杂度为mlogm
poj2417
题意:mod为素数,对于问题 b ^ x == N % mod 的问题,知道b,N,mod,求最小指数 x;
思路: mod为素数的话求b的逆元可以用快速幂pow_mod(b,mod - 2,mod),后者用拓展欧几里得;其他一致
用hash表进行求解是标准的解法:
#include
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
//欧几里得
ll gcd(ll x,ll y)
{
return y ? gcd(y,x % y) : x;
}
//扩展欧几里得
void ex_gcd(ll a,ll b,ll & d,ll & x,ll & y)
{
if(b == 0)
{
d = a; x = 1;y = 0;
return ;
}
ex_gcd(b,a % b,d,y,x);
y -= a / b * x;
}
//快速幂取模
ll pow_mod(ll x,ll n,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(n)
{
if(n & 1)
{
ans = ans * t % mod_val;
}
n >>= 1;
t = t * t % mod_val;
}
return ans;
}
//逆元
ll Inv(ll a, ll mod_val)
{
ll x,y,d;
ex_gcd(a,mod_val,d,x,y);
return d == 1 ? (x % mod_val + mod_val) % mod_val : -1;
}
//大数相乘取模
ll Mul(ll a,ll b,ll mod_val)
{
a %= mod_val;
b %= mod_val;
ll ans = 0;
ll t = a;
while(b)
{
if(b & 1)
{
ans = (ans + t) % mod_val;
}
b >>= 1;
t <<= 1;
}
return ans;
}
//哈希表
const ll hash_mod = 9876543;
ll key[hash_mod],val[hash_mod];
int head[hash_mod],next[hash_mod];
struct Hash
{
int tot;
void Init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void inserts(ll x,ll y)
{
ll k = x % hash_mod;
key[tot] = x;
val[tot] = y;
next[tot] = head[k];
head[k] = tot ++;
}
ll finds(ll x)
{
ll k = x % hash_mod;
for(ll i = head[k]; i != -1; i = next[i])
{
if(key[i] == x)
{
return val[i];
}
}
return -1;
}
}hs;
//求解a^x = b %(mod)已知其他三个,求x;
ll baby_giant(ll a,ll b,ll mod_val)
{
hs.Init();
ll m = ceil(sqrt((double)mod_val));
ll cur = 1;
//baby
for(ll j = 0; j < m; j ++)
{
if(hs.finds(cur) == -1)
{
hs.inserts(cur,j);
}
cur = cur * a % mod_val;
}
//a^-m %mod_val,这是mod_val为素数的时候的求法
ll _Am = pow_mod(pow_mod(a,mod_val - 2,mod_val),m,mod_val);
cur = 1;
//giant
for(int i = 0; i < m; i ++)
{
ll j = hs.finds(cur * b % mod_val);
if(j != -1)
{
return i *m + j;
}
cur = cur * _Am % mod_val;
}
return -1;
}
//求解a ^x = b %mod ,已知其他三个,求a;
ll a,mod,b;
int main()
{
int T = 1;
while( ~ scanf("%I64d%I64d%I64d",&mod,&a,&b))
{
// cout << "case"<
map记录位置时间长一些:
#include
#include
#include
#include
#include
#include
#include
用数组记录二分的话实践短很多:
PS:写二分的时候记得找到值不能立即返回,记录下来,可能还有更小的j,
PS: 不知用map建的表会超时,结果用数组记录,二分查找,时间节省了这么多,实践证明不要用map去记录下标,那要很费时间,还是用数组吧;
#include
#include
#include
#include
#include
#include
using namespace std;
#define clr(x,y) memset(x,y,sizeof x)
const int maxn = 1000000 + 10;
const double eps = 10e-10;
const int mod = 10e6 + 10;
typedef long long ll;
ll prime[maxn];
struct Baby
{
ll b,j;
}baby[maxn];
bool cmp(const Baby &x,const Baby & y)
{
return x.b != y.b ? (x.b < y.b) : (x.j < y.j);
}
ll get_prime(ll x)
{
ll cnt = 0;
for(ll i = 2; i<= x; i ++)
{
if(x % i == 0)
{
prime[cnt ++] = i;
while(x % i == 0)
x /= i;
}
}
if(x > 1)
prime[cnt ++] = x;
return cnt;
}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(b == 0)
{
d = a; x = 1;y = 0;
return ;
}
ex_gcd(b,a % b,d,y,x);
y -= x * a/b;
}
ll pow_mod(ll x,ll y,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(y)
{
if(y & 1)
{
ans = ans * t % mod_val;
}
y >>= 1;
t = (t * t) % mod_val;
}
return ans;
}
ll inv(ll a,ll mod_val)
{
ll x,y,d;
ex_gcd(a,mod_val,d,x,y);
return d == 1 ? (x % mod_val + mod_val) % mod_val : -1;
}
ll finds(ll x,ll m)
{
ll l = 0,r = m- 1;
ll ans = -1;
while(r >= l)
{
ll mid = (l + r) / 2;
if(baby[mid].b > x)
{
if(baby[mid].b == x)//保证找到的j是最小的
ans = baby[mid].j;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
return ans;
}
ll baby_gaint(ll b,ll n,ll p)
{
ll m = (ll)ceil(sqrt((double)(p -1)));
// cout << m << endl;
ll ans = 1;
//baby_step
for(ll j = 0; j < m; j++)
{
baby[j].j = j;
baby[j].b = ans;
ans = ans * b % p;
}
sort(baby,baby+m,cmp);
//求逆元:当mod为素数的时候,a % mod 的逆元为pow_mod(a,mod - 2,mod),后者用ex_gcd求;
ll invs = pow_mod(b,p - 2,p);
// b^(-m) %mod
ll _bm = pow_mod(invs,m,p);
// cout << _bm << endl;
//gaint_step
ans = 1;
//大步走m,然后找到与之相等的baby对应的j值
for(ll i = 0; i < m; i ++)
{
ll j = finds(n * ans % p,m);
if(j != -1)
{
// cout <
2.当mod不为素数的时候,用ex_baby_step_gaint_step算法;
算法过程如下:
扩展小步大步攻击:A ^ x = B (mod C)
1 : i = 0-> 100 if A^i mod C == B return i;///做这一步是第四步有一个n
2 : 消因子, 将A^x = B mod C 划为 d * A ^ (x – n) = B (modC)
3 : m = ceil (sqrt(C))
4 : 将 A ^ (x - n) 化为 A ^ (I * m + j)(原x 化为了 n + I * m + j)这里与小步大步攻击不同
5 : for j = 0 ->m hash(j,A ^ j mod C)
6 : for i = 0 ->m AA = B/(d * A ^ m ^i)
7 :在hash表中查找AA,若有,取AA对应的j,则答案为I * m + j + n;
这里令x - n = i * m + j,则 A ^j = B * A ^(-i * m) * d ^(-1) % C;求解过程就跟朴素的那个baby_step_gaint_step一样了;
poj3243
hash:
#include
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
//欧几里得
ll gcd(ll x,ll y)
{
return y ? gcd(y,x % y) : x;
}
//扩展欧几里得
void ex_gcd(ll a,ll b,ll & d,ll & x,ll & y)
{
if(b == 0)
{
d = a; x = 1;y = 0;
return ;
}
ex_gcd(b,a % b,d,y,x);
y -= a / b * x;
}
//快速幂取模
ll pow_mod(ll x,ll n,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(n)
{
if(n & 1)
{
ans = ans * t % mod_val;
}
n >>= 1;
t = t * t % mod_val;
}
return ans;
}
//逆元
ll Inv(ll a, ll mod_val)
{
ll x,y,d;
ex_gcd(a,mod_val,d,x,y);
return d == 1 ? (x % mod_val + mod_val) % mod_val : -1;
}
//大数相乘取模
ll Mul(ll a,ll b,ll mod_val)
{
a %= mod_val;
b %= mod_val;
ll ans = 0;
ll t = a;
while(b)
{
if(b & 1)
{
ans = (ans + t) % mod_val;
}
b >>= 1;
t <<= 1;
}
return ans;
}
//哈希表
const ll hash_mod = 9876543;
ll key[hash_mod],val[hash_mod];
int head[hash_mod],nexts[hash_mod];
struct hash
{
int tot;
void Init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void inserts(ll x,ll y)
{
ll k = x % hash_mod;
key[tot] = x;
val[tot] = y;
nexts[tot] = head[k];
head[k] = tot ++;
}
ll finds(ll x)
{
ll k = x % hash_mod;
for(ll i = head[k]; i != -1; i = nexts[i])
{
if(key[i] == x)
{
return val[i];
}
}
return -1;
}
}hs;
//求解a^x = b %(mod)已知其他三个,求x;
ll baby_giant(ll a,ll b,ll mod_val)
{
hs.Init();
ll m = ceil(sqrt((double)mod_val));
ll cur = 1;
//baby
for(ll j = 0; j < m; j ++)
{
if(hs.finds(cur) == -1)
{
hs.inserts(cur,j);
}
cur = cur * a % mod_val;
}
//a^-m %mod_val,这是mod_val为素数的时候的求法
ll _Am = pow_mod(pow_mod(a,mod_val - 2,mod_val),m,mod_val);
cur = 1;
//giant
for(int i = 0; i < m; i ++)
{
ll j = hs.finds(cur * b % mod_val);
if(j != -1)
{
return i *m + j;
}
cur = cur * _Am % mod_val;
}
return -1;
}
ll ex_baby_giant(ll a,ll b, ll mod_val)
{
hs.Init();
for(ll i = 1; i <= 100; i ++)
{
if(pow_mod(a,i,mod_val) == b % mod_val)
return i;
}
ll g,d = 1,n = 0;
while((g = gcd(a,mod_val)) != 1)
{
if(b % g)
return -1;
b /=g;
mod_val /= g;
n ++;
d = d * (a / g) % mod_val;
}
ll m = ceil(sqrt(double(mod_val)));
//baby
ll cur = 1;
for(ll j = 0; j < m; j ++)
{
if(hs.finds(cur) == -1)
{
hs.inserts(cur,j);
}
cur = cur * a % mod_val;
}
//d^-1
ll _d = Inv(d,mod_val);
//d ^ -m
ll _bm = pow_mod(Inv(a,mod_val),m,mod_val);
cur = 1;
//giant
for(ll i = 0; i < m; i ++)
{
ll j = hs.finds(cur * _d % mod_val * b % mod_val);
if(j != -1)
{
return i * m + j + n;
}
cur = cur * _bm % mod_val;
}
return -1;
}
//求解a ^x = b %mod ,已知其他三个,求a;
ll a,mod,b;
int main()
{
int T = 1;
while( ~ scanf("%I64d%I64d%I64d",&a,&mod,&b))
{
if(!a && !b && !mod)
break;
// cout << "case"<= mod)
// {
// cout << "No solution" << endl;
// continue;
// }
ll ans = ex_baby_giant(a,b,mod);
if(ans != -1)
cout << ans << endl;
else cout << "No Solution" << endl;
}
return 0;
}
数组二分:
#include
#include
#include
#include
#include
#include
#include
hdu2815
注意k >= z的时候进行返回找不到
#include
#include
#include
#include
#include
#include
#include
下面给出baby_step_giant_step和ex_baby_step_giant_step的模板:
ll gcd(ll x,ll y)
{
return y ? gcd(y,x % y) : x;
}
//扩展欧几里得
void ex_gcd(ll a,ll b,ll & d,ll & x,ll & y)
{
if(b == 0)
{
d = a; x = 1;y = 0;
return ;
}
ex_gcd(b,a % b,d,y,x);
y -= a / b * x;
}
//快速幂取模
ll pow_mod(ll x,ll n,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(n)
{
if(n & 1)
{
ans = ans * t % mod_val;
}
n >>= 1;
t = t * t % mod_val;
}
return ans;
}
//逆元
ll Inv(ll a, ll mod_val)
{
ll x,y,d;
ex_gcd(a,mod_val,d,x,y);
return d == 1 ? (x % mod_val + mod_val) % mod_val : -1;
}
//大数相乘取模
ll Mul(ll a,ll b,ll mod_val)
{
a %= mod_val;
b %= mod_val;
ll ans = 0;
ll t = a;
while(b)
{
if(b & 1)
{
ans = (ans + t) % mod_val;
}
b >>= 1;
t <<= 1;
}
return ans;
}
//哈希表
const ll hash_mod = maxn * 10;
ll key[hash_mod],val[hash_mod];
int head[hash_mod],nexts[hash_mod];
struct hash
{
int tot;
void Init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void inserts(ll x,ll y)
{
ll k = x % hash_mod;
key[tot] = x;
val[tot] = y;
nexts[tot] = head[k];
head[k] = tot ++;
}
ll finds(ll x)
{
ll k = x % hash_mod;
for(ll i = head[k]; i != -1; i = nexts[i])
{
if(key[i] == x)
{
return val[i];
}
}
return -1;
}
}hs;
//求解a^x = b %(mod)已知其他三个,求x;
ll baby_giant(ll a,ll b,ll mod_val)
{
hs.Init();
ll m = ceil(sqrt((double)mod_val));
ll cur = 1;
//baby
for(ll j = 0; j < m; j ++)
{
if(hs.finds(cur) == -1)
{
hs.inserts(cur,j);
}
cur = cur * a % mod_val;
}
//a^-m %mod_val,这是mod_val为素数的时候的求法
ll _Am = pow_mod(pow_mod(a,mod_val - 2,mod_val),m,mod_val);
cur = 1;
//giant
for(int i = 0; i < m; i ++)
{
ll j = hs.finds(cur * b % mod_val);
if(j != -1)
{
return i *m + j;
}
cur = cur * _Am % mod_val;
}
return -1;
}
ll ex_baby_giant(ll a,ll b, ll mod_val)
{
hs.Init();
for(ll i = 1; i <= 100; i ++)
{
if(pow_mod(a,i,mod_val) == b % mod_val)
return i;
}
ll g,d = 1,n = 0;
while((g = gcd(a,mod_val)) != 1)
{
if(b % g)
return -1;
b /=g;
mod_val /= g;
n ++;
d = d * (a / g) % mod_val;
}
ll m = ceil(sqrt(double(mod_val)));
//baby
ll cur = 1;
for(ll j = 0; j < m; j ++)
{
if(hs.finds(cur) == -1)
{
hs.inserts(cur,j);
}
cur = cur * a % mod_val;
}
//d^-1
ll _d = Inv(d,mod_val);
//d ^ -m
ll _bm = pow_mod(Inv(a,mod_val),m,mod_val);
cur = 1;
//giant
for(ll i = 0; i < m; i ++)
{
ll j = hs.finds(cur * _d % mod_val * b % mod_val);
if(j != -1)
{
return i * m + j + n;
}
cur = cur * _bm % mod_val;
}
return -1;
}