离散对数求解算法

本文介绍了离散对数问题的解决算法——baby_step_giant_step,以及其扩展形式用于处理非素数模的情况。通过预处理、二分查找和快速幂等技术,实现了O(sqrt(P)*log(sqrt(P)))时间复杂度的求解过程。同时,讨论了原根和扩展小步大步攻击算法,包括原根的定义、性质、判定定理及求解步骤,最后给出了相关样题和代码模板。


求解一个最小的x满足给定的方程Bx == N (mod P)

使用baby_step_giant_step算法。也就是先小步后大步算法。

1、令x=i*m+j  (m=ceil(sqrt(p))),

那么原式化为 B^(i*m)*B^j==N(MOD P)

B^j==N*B^(-i*m)(MOD P)---------->B^j==N*B^m^(-i)(MOD P)

2、先预处理B^0,B^1,B^2……B^(m-1),存入HASH表,我使用结构体排序然后二分查找,这一步就是baby_step,每次移动1

3、然后快速幂求出B^-m,枚举i,如果存在N*B^(-i*m)存在于HASH表中,说明存在解x=i*m+j,这一步为giant_step,每次移动m

注意以上解法是最基本的,只能对于gcd(B,P)==1,算法的时间复杂度是O(sqrt(P)*log(sqrt(P)))

样题:poj2417

ps:参考cxlove博客,密码学里面貌似叫做shanks算法 点击打开链接

模板:

///大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
}babyv[100005];
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num){
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///B^x=N(modP)求解x,无解返回-1
long long babystep_giantstep(long long B,long long N,long long P)
{
    long long m=ceil(sqrt(P)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*B)%P;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long _Bm=q_pow(q_pow(B,P-2,P),m,P);///算出B^(-m)%P的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(N*ans%P,m);///二分查找
        if(j!=-1)return i*m+j;///找到返回答案
        ans=(ans*_Bm)%P;///继续递推
    }
    return -1;///找不到答案
}

poj2417代码:点击打开链接

#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Inf (1<<29)
#define LL long long
using namespace std;
const int MM=110;
const long long MOD=1000000007;
///大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        return b<other.b;
    }
}babyv[100005];
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b==num)return babyv[mid].j;
        else if(babyv[mid].b>num)r=mid-1;
        else l=mid+1;
    }
    return -1;
}
///B^x=N(modP)求解x,无解返回-1
long long babystep_giantstep(long long B,long long N,long long P)
{
    long long m=ceil(sqrt(P-1)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*B)%P;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long _Bm=q_pow(q_pow(B,P-2,P),m,P);///算出B^(-m)%P的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(N*ans%P,m);///二分查找
        if(j!=-1)return i*m+j;///找到返回答案
        ans=(ans*_Bm)%P;///继续递推
    }
    return -1;///找不到答案
}
int main()
{
    long long B,N,P;
    //freopen("D:\\o.txt","r",stdin);
    while(~scanf("%lld%lld%lld",&P,&B,&N))
    {
        long long ans=babystep_giantstep(B,N,P);
        if(ans==-1)puts("no solution");
        else printf("%lld\n",ans);
    }
    return 0;
}
 

求解一个最小的x满足给定的方程Ax == B (mod C)

其中C任意,即C不一定是素数

算法 : 扩展小步大步攻击

扩展小步大步攻击: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

样题:poj3243 hdu2815

ps:参考点击打开链接有详细证明的方法

模板:

///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
}babyv[100005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
     long long d;
     if(b == 0) {x = 1;     y = 0;     return a;}
     d = extended_euclid(b, a % b, y, x);
     y -= a / b * x;
     return d;
}
long long Inv(long long a,long long n)
{
     long long d, x, y;
     d = extended_euclid(a, n, x, y);
     if(d == 1)	return (x%n + n) % n;
     else			return -1; // no solution
}
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num){
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
    for(long long i=0;i<=100;i++)///先在小于100的数里面测试
        if(q_pow(A,i,C)==B%C)return i;
    ///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
    long long g,n=0,d=1;
    while((g=gcd(A,C))!=1)
    {
        if(B%g)return -1;///无解
        n++;
        B/=g;
        C/=g;
        d=(d*A/g)%C;
    }
    ///无扩展小步大步操作
    long long m=ceil(sqrt(C)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*A)%C;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(Inv(d,C)*B%C*ans%C,m);///二分查找
        if(j!=-1)return i*m+j+n;///找到返回答案
        ans=(ans*Bm)%C;///继续递推
    }
    return -1;///找不到答案
}


poj3243点击打开链接

#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Inf (1<<29)
#define LL long long
using namespace std;
const int MM=110;
const long long MOD=1000000007;
///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
}babyv[100005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
     long long d;
     if(b == 0) {x = 1;     y = 0;     return a;}
     d = extended_euclid(b, a % b, y, x);
     y -= a / b * x;
     return d;
}
long long Inv(long long a,long long n)
{
     long long d, x, y;
     d = extended_euclid(a, n, x, y);
     if(d == 1)	return (x%n + n) % n;
     else			return -1; // no solution
}
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num){
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
    for(long long i=0;i<=100;i++)///先在小于100的数里面测试
        if(q_pow(A,i,C)==B%C)return i;
    ///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
    long long g,n=0,d=1;
    while((g=gcd(A,C))!=1)
    {
        if(B%g)return -1;///无解
        n++;
        B/=g;
        C/=g;
        d=(d*A/g)%C;
    }
    ///无扩展小步大步操作
    long long m=ceil(sqrt(C)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*A)%C;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(Inv(d,C)*B%C*ans%C,m);///二分查找
        if(j!=-1)return i*m+j+n;///找到返回答案
        ans=(ans*Bm)%C;///继续递推
    }
    return -1;///找不到答案
}
int main()
{
    long long X,Z,K;
    //freopen("D:\\o.txt","r",stdin);
    while(~scanf("%lld%lld%lld",&X,&Z,&K))
    {
        if(X==0&&Z==0&&K==0)break;
        long long ans=ex_babystep_giantstep(X,K,Z);
        if(ans==-1)puts("No Solution");
        else printf("%lld\n",ans);
    }
    return 0;
}




hdu2815 点击打开链接

注意:K>=Z


#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Inf (1<<29)
#define LL long long
using namespace std;
const int MM=110;
const long long MOD=1000000007;
///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
}babyv[100005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
     long long d;
     if(b == 0) {x = 1;     y = 0;     return a;}
     d = extended_euclid(b, a % b, y, x);
     y -= a / b * x;
     return d;
}
long long Inv(long long a,long long n)
{
     long long d, x, y;
     d = extended_euclid(a, n, x, y);
     if(d == 1)	return (x%n + n) % n;
     else			return -1; // no solution
}
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num){
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
    for(long long i=0;i<=100;i++)///先在小于100的数里面测试
        if(q_pow(A,i,C)==B%C)return i;
    ///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
    long long g,n=0,d=1;
    while((g=gcd(A,C))!=1)
    {
        if(B%g)return -1;///无解
        n++;
        B/=g;
        C/=g;
        d=(d*A/g)%C;
    }
    ///无扩展小步大步操作
    long long m=ceil(sqrt(C)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*A)%C;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(Inv(d,C)*B%C*ans%C,m);///二分查找
        if(j!=-1)return i*m+j+n;///找到返回答案
        ans=(ans*Bm)%C;///继续递推
    }
    return -1;///找不到答案
}
int main()
{
    long long X,Z,K;
    while(~scanf("%I64d%I64d%I64d",&X,&Z,&K))
    {
        //if(X==0&&Z==0&&K==0)break;
        if(K>=Z){puts("Orz,I can’t find D!");continue;}
        long long ans=ex_babystep_giantstep(X,K,Z);
        if(ans==-1)puts("Orz,I can’t find D!");
        else printf("%I64d\n",ans);
    }
    return 0;
}

给定k,m,newx,其中m是素数,求出满足x^k mod m = newx 所有 x 的值

预备:

a、模mod的群G的元的阶的定义:a^m%mod==e的最小整数m叫做元a的阶
b、原根(本原元素)的定义:a^phi(m)%m==e,phi(m)是a的阶,则称a是模m的一个原根

原根的性质:{1,2,...,m-1} = {g,g^2,...,g^(m-1)}其中g是原根

原根的判定定理(欧拉定理):p(大于2)是一个素数,a是本原元素当且仅当a的(p-1)╱q次方(模p)不等于1,q是p-1的所有因子

求解问题的算法步骤如下

1) 先暴力求m的原根g
 2) 大步小步求g^t = newx mod m
 3) 则g^(t+v*m1) = newx mod m, m1 = p - 1
 4) 设x = g^u mod p, x^k = (g^u)^k = g^(uk) = g^(t+v*m1)

化成uk+vm=t,可以用扩展欧几里得求得

样题:hdu3930 点击打开链接

参考自点击打开链接

代码如下:

#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long cnt=0;
long long p[2000001];
bool isprime[2000001];
///求a*b%c,因为a,b很大,所以要先将b写成二进制数
long long mul_mod(long long a, long long b, long long mod )
{
    long long ans = 0, d = a % mod;
    while( b )
    {
        if( b & 1 )
        {
            ans += d;
            if( ans > mod )ans -= mod;
        }
        d += d;
        if( d > mod )d -= mod;
        b >>= 1;
    }
    return ans;
}
void init()
{
    isprime[0]=isprime[1]=true;
    for(int i=2; i<=1000000; i++)
    {
        if(isprime[i])continue;
        p[cnt++]=i;
        for(int j=i+i; j<=1000000; j+=i)
            isprime[j]=true;
    }
}
long long num=0;
long long f[200];
void factor(long long n)
{
    num=0;
    for(long long i=0; i<cnt&&p[i]*p[i]<=n; i++)
    {
        if(n%p[i]==0)
        {
            f[num++]=p[i];
            while(n%p[i]==0)n/=p[i];
        }
    }
    if(n>1)f[num++]=n;
}
///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const
    {
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
} babyv[4000005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
    long long d;
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    d = extended_euclid(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
long long Inv(long long a,long long n)
{
    long long d, x, y;
    d = extended_euclid(a, n, x, y);
    if(d == 1)	return (x%n + n) % n;
    else			return -1; // no solution
}
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        //if(y&1)ans=(ans*x)%mod;
        if(y&1)ans=mul_mod(ans,x,mod);
        //x=(x*x)%mod;
        x=mul_mod(x,x,mod);
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num)
        {
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
    for(long long i=0; i<=100; i++) ///先在小于100的数里面测试
        if(q_pow(A,i,C)==B%C)return i;
    ///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
    long long g,n=0,d=1;
    while((g=gcd(A,C))!=1)
    {
        if(B%g)return -1;///无解
        n++;
        B/=g;
        C/=g;
        //d=(d*A/g)%C;
        d=mul_mod(d,A/g,C);
    }
    ///无扩展小步大步操作
    long long m=ceil(sqrt(C+0.0)),ans=1;
    for(long long j=0; j<m; j++) ///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        //ans=(ans*A)%C;///递推
        ans=mul_mod(ans,A,C);
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
    for(long long i=0; i<m; i++)
    {
        long long temp=mul_mod(Inv(d,C),B,C);//Inv(d,C)*B%C*ans%C
        temp=mul_mod(temp,ans,C);
        long long j=find(temp,m);///二分查找
        if(j!=-1)return i*m+j+n;///找到返回答案
        //ans=(ans*Bm)%C;///继续递推
        ans=mul_mod(ans,Bm,C);
    }
    return -1;///找不到答案
}
bool judge(long long g,long long mod)
{
    long long i;
    for(i=0; i<num; i++)
        if(q_pow(g,(mod-1)/f[i],mod)==1)break;
    if(i>=num)return true;
    return false;
}
long long find_g(long long mod)
{
    factor(mod-1);
    for(long long g=2;; g++)
        if(judge(g,mod))return g;
}
vector<long long>res;
int main()
{
    init();
    long long Case=1;
    long long newx,k,m;
    while(scanf("%I64d%I64d%I64d",&k,&m,&newx)!=EOF)
    {
        printf("case%I64d:\n",Case++);
        long long u,v;
        long long g=find_g(m);
        long long t=ex_babystep_giantstep(g,newx,m);
        long long GCD=extended_euclid(k,m-1,u,v);
        if(t%GCD||newx>=m)puts("-1");
        else
        {
            res.clear();
            long long up=t/GCD;
            long long down=(m-1)/GCD;
            u=(u*up%down+down)%down;
            for(long long i=0; i<GCD; i++)
            {
                long long temp=u+down*i;
                res.push_back(q_pow(g,temp,m));
            }
            sort(res.begin(),res.end());
            for(long long i=0; i<res.size(); i++)
                printf("%I64d\n",res[i]);
        }
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值