高次同余方程模板BabyStep-GiantStep

/*************************************
 ---高次同余方程模板BabyStep-GiantStep---

 输入:对于方程A^x=B(mod C),调用BabyStep(A,B,C),(0<=A,B,C<=10^9)

 输出:无解放回-1,有解放回最小非负整数x

 复杂度:O(C^0.5),只与C有关,与A,B的大小无关
 ************************************/

typedef long long ll;
#define HASH_N 100007

struct hashnode
{
    int next;
    ll key;
    int j;
}HashLink[ HASH_N ];

int hashpre[ HASH_N ],hashcnt;

void hash_insert(ll x,ll key,int j)
{
    for(int p=hashpre[x];p!=-1;p=HashLink[p].next)
    {
        if(HashLink[p].key==key) return ;
    }
    HashLink[ hashcnt ].key = key;
    HashLink[ hashcnt ].j = j;
    HashLink[ hashcnt ].next = hashpre[x];
    hashpre[x] = hashcnt++;
}

int hash_get(ll key)
{
    ll x=key%HASH_N;
    for(int p=hashpre[x];p!=-1;p=HashLink[p].next)
    {
        if( HashLink[p].key == key ) return HashLink[p].j;
    }
    return -1;
}

ll gcd(ll a,ll b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}

//ax + by = gcd(a,b)
//传入固定值a,b.放回 d=gcd(a,b), x , y
void extendgcd(long long a,long long b,long long &d,long long &x,long long &y)
{
    if(b==0){d=a;x=1;y=0;return;}
    extendgcd(b,a%b,d,y,x);
    y-=x*(a/b);
}

//Ax=1(mod M)
//返回x的范围是[0,M-1]
ll GetNi(ll A,ll M)
{
    ll rex=0,rey=0;
    ll td=0;
    extendgcd(A,M,td,rex,rey);
    return (rex%M+M)%M;
}

//a^b%mod 快速幂
long long Quk_Mul(long long a,long long b,long long mod)
{
    long long qsum=1;
    while(b)
    {
        if(b&1) qsum=(qsum*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return qsum;
}

//测试x较小的情况,必须!
ll firsttest(ll A,ll B,ll C)
{
    ll tmp=1;
    if(B==1) return 0;
    for(int i=1;i<100;i++)
    {
        tmp = (tmp*A)%C;
        if(tmp==B) return i;
    }
    return -1;
}

//欧拉函数 返回[1,x-1]中与x互素的数的个数,复杂度n^0.5
ll euler(ll x)
{
    ll i, res=x;
    for (i = 2; i < (ll)sqrt(x * 1.0) + 1; i++)
        if(x%i==0)
        {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);
    return res;
}

ll BabyStep(ll A,ll B,ll C)
{
    if(0==A || 0==C) return -1;
    if(C==1) return 0;
    B = B%C;
    ll ans = firsttest(A,B,C);//为了防止x比较小的时候
    if(ans != -1) return ans;
    ll D=1;
    int c=0;
    ll d;
    while( (d=gcd(A,C)) != 1 )
    {
        if( B%d !=0 ) return -1;//无解的情况
        B /= d;
        C /= d;
        D = D*A/d%C;
        c++;
    }

    //得到了 D*A^(x-c)=B (mod C) ,gcd(A,C)=1 , gcd(D,C)=1
    ll D_1=GetNi(D,C);//求D的逆元
    B = B*D_1%C;
    //求A^x=B (mod C),然后返回x+c
    ll m = ceil( sqrt(C+0.0) );

    memset(hashpre,-1,sizeof(hashpre));
    hashcnt=0;
    ll hashnum=1;
    hash_insert(1, 1, 0);
    for(int i=1;i<m;i++)
    {
        hashnum = (hashnum*A)%C;
        hash_insert(hashnum%HASH_N, hashnum ,i);
    }

    ll ol=euler(C);
    ll mA=Quk_Mul(A, m, C);
    ll ta=1;

    ll tmpni = Quk_Mul(mA, ol-1, C);

    for(int i=0;i<m;i++,ta=ta*tmpni%C)
    {
        //解线性同余方程 tx=B*ta (%C) ,ta直接用费马小定理求逆元
        ll tx = ta;
        tx = (tx*B)%C;
        int j=hash_get(tx);
        if(j!=-1)//找到解了
        {
            return i*m+j+c;
        }
    }

    return -1;
}

其实还是很很正常的解法,这个甚至可以当成一道难一点的数论题目做。

关于详细的解释。

时间: 2024-08-16 18:47:57

高次同余方程模板BabyStep-GiantStep的相关文章

POJ2417 Discrete Logging【高次同余方程】

题目链接: http://poj.org/problem?id=2417 题目大意: 已知整数P.B.N满足公式B^i = N(mod P),求i的值是多少. 思路: 典型的解高次同余方程A^x = B(mod C),直接套模板解决.注意输入顺序:C A B AC代码: #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath>

数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)

什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSGS算法中是要求a^m在%c条件下的逆元的,如果a.c不互质根本就没有逆元.) 如果x有解,那么0<=x<C,为什么? 我们可以回忆一下欧拉定理: 对于c是素数的情况,φ(c)=c-1 那么既然我们知道a^0=1,a^φ(c)=1(在%c的条件下).那么0~φ(c)必定是一个循环节(不一定是最小的)

poj2417(Baby-Step Giant-Step)

题目链接:http://poj.org/problem?id=2417 题意:求满足给出 P, N, B, 求满足条件 BL == N (mod P) 的最小 L, 若不存在则输出 no solution. 思路:Baby-Step Giant-Step 算法 设 L = kt ? m,其中 t = ?sqrt(L)?, 0 <= m < t.那么 B^L = N (mod P) 就等价于 B^(kt ? m) = N (mod P) 即 B^(kt) ? N^(?1) = B^m (mod

Discrete Log Algorithms :Baby-step giant-step

离散对数的求解 1.暴力 2.Baby-step giant-step 3.Pollard's ρ algorithm -- 下面搬运一下Baby-step giant-step 的做法 这是在 https://ctf-wiki.github.io/ctf-wiki/crypto/asymmetric/discrete-log/discrete-log/上看到的,比较容易理解. 而且,里面的代码写得简洁明了. 写一下自己理解和自己照着写了一遍 原文代码: def bsgs(g, y, p): m

POJ3243 Clever Y【高次同余方程】

题目链接: http://poj.org/problem?id=3243 题目大意: 已知公式A^x mod C= B,以及A.C.B的值,求解x的值为多少. 思路: 典型的求解方程A^x = B(mod C),直接模板解决. AC代码: #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #define LL __in

【poj3243-Clever Y】高次同余方程-拓展BabyStepGiantStep

http://poj.org/problem?id=3243 题意:给定X,Z,K,求一个最小的Y满足XY mod Z = K. 题解:BSGS的拓展版本(因为X和Z不一定互质).这道题挺坑的,如果K>=Z不是输出无解而是让K%=Z. 算是BSGS的模板题,我打了两种版本,就是二分查找和hash.对比两次提交来看,二分省空间,耗时间:Hash省时间,耗空间. 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstrin

高次同余方程 专题

大牛详解算法 不过cxlove大牛的写法不是hash表实现(读书少,说错了请指正),弱渣用hash表实现了一下..... hdu 2815 mod tree http://acm.hdu.edu.cn/showproblem.php?pid=2815 几个坑点:输出can‘t上面的确是全角,不是半角,手残手动改成了半角.....还有一个地方就是余数n的值应该小于mod的值..... /********************************************************

HDU2815 Mod Tree【高次同余方程】

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2815 题目大意: 有一颗树,每个节点有K个儿子,那么问题来了:能否算出这棵树的最小深度D,使得这个深度 的节点数对P取模的结果为N吗? 思路: 转换一下题目含义,就变成了解K^i = N(mod P),典型的A^i = B(mod C)问题,此题B的范围 明显在[0,C-1]之间,若不在此区间,方程显然无解. AC代码: #include<iostream> #include<algori

高次同余方程 $BSGS$

第一篇\(Blog\)... 还是决定把\(luogu\)上的那篇搬过来了. BSGS,又名北上广深 它可以用来求\(a^x \equiv b (mod \ n)\)这个同余方程的一个解,其中\(a,n\)互质. 欧拉定理告诉我们,这里\(a^{\varphi(n)} \equiv 1 (mod \ n)\) 由于\(a^0 \equiv 1 (mod \ n)\),所以这里\(x\)到\(\varphi(n)\)后\(a^x \ mod \ n\)就开始循环了. 所以我们最坏情况就是\(n\)