原根与BSGS

我是鸽子。


$ \mathbf{原根}$



根据欧拉定理,对于 \(n\in \mathbb N^* ,a\in \mathbb Z\)\(\gcd(a,n)=1\) ,有 \(a^{\varphi (n) }\equiv 1 \pmod n\)

此时满足 \(a^{x}\equiv 1 \pmod n\) 的最小正整数存在,\(x\) 称作 \(a\)\(n\) 的阶,记作 \(\delta_{n}(a)\)

性质

不证了,OI-wiki 上都有。

  1. \(a,a^2,\cdots,a^{\delta_n(a)}\)\(n\) 不同余。

  2. \(a^m\equiv 1 \pmod n\) ,则 \(\delta_n(a) | n\)

  3. \(n\in \mathbb N^* ,a,b\in \mathbb Z\)\(\gcd(a,n)=1,gcd(b,n)=1\) ,那么 \(\delta_n(ab)=\delta_n(a)\delta_n(b)\) 的充要条件是 \(gcd(\delta_n(a),\delta_n(b))=1\)

  4. \(k\in \mathbb N^*, n\in \mathbb N^* ,a\in \mathbb Z\) ,则 \(\delta_n (a^k)=\frac{\delta_n (a)}{gcd(\delta_n (a),k)}\)

原根

\(n\in \mathbb N^* ,a\in \mathbb Z\)\(\gcd(a,n)=1,\delta_n(a)=\varphi(n)\),那么称 \(a\)\(n\) 的一个原根。

原根个数

若某个整数 \(n\) 有原根,则它原根的个数为 \(\varphi(\varphi(n))\)

原根存在定理

只有 \(2,4,p^k,2p^k\) 存在原根,其中 \(p\) 为奇质数。

原根判定定理

\(n \ge 3,\gcd(a,n)=1\)\(a\)\(n\) 的一个原根当且仅当对于 \(\varphi(n)\) 的所有质因子 \(p\) 都有 \(a^{\frac{\varphi(n)}{p}} \not\equiv 1 \pmod n\)

\(n\) 的最小原根是不多于 \(O(n^{0.25})\) 级别的,因此可以直接从小到大枚举。

利用原根求解底数同余方程

题意:

给定 \(k,a,p\),求 \(x^k \equiv a \pmod p\) 的所有根,\(p\) 为质数,根的范围是 \([0,p-1]\)

\(0\le a<p\le 10^9 ,2\le k\le 10^6\)

思路:

求出 \(p\) 的原根 \(g\),设 \(x=g^t\) ,则有:

\[(g^t)^k \equiv a \pmod p \]

\[(g^k)^t \equiv a \pmod p \]

现在已知 \(g^k\) ,用 \(\operatorname{BSGS}\) 求解出 \(t\) 的所有解即可。

或者先用 \(\operatorname{BSGS}\) 求出 \(g^{m} \equiv a \pmod p\) 的最小解,再通过 \(\operatorname{exgcd}\) 求二元不定方程 $m+x\varphi(p)=kt $ 中 \(t\) 的所有解。





$ \mathbf{BSGS}$



普通BSGS

题意:

给定非负整数 \(a,b\) 以及质数 \(p\),求 \(a^x\equiv b \pmod p\) 的最小非负整数解,若无解则输出 no solution

$ a,b,p <2^{31}$

思路:

根据费马小定理,质数 \(p\) 不整除 \(a\) 的情况下存在 \(a^{p-1}\equiv 1 \pmod p\),得知解的范围应该是 \([0,p-1)\),直接枚举将会有一个感人的复杂度。

考虑设 \(t=\lceil \sqrt p \rceil\) ,将解 \(x\) 表示成 \(i\times t-j\) 的形式:

\[a^{i\times t -j}\equiv b \pmod p \]

两边同时乘以 \(a^{j}\)

\[a^{i\times t }\equiv b\times a^j \pmod p \]

提前用 map 或者 unordered_map 将右侧的值 \(b\times a^j\) 映射为 \(j\),然后枚举 \(i\) ,对于 \(a^{i\times t }\)map 中查找是否存在 \(b\times a_j\) 与其同余,若没有则跳过,若有则答案即为 $i\times t-j $ ,一直到最后输出无解。

\(j\) 从小到大映射时,相同值所代表的 \(j\) 一定是更大的,于是 \(i\times t-j\) 一定会更小,符合最小非负整数解的条件。

映射的范围和枚举的上界都是 \(\lceil \sqrt p \rceil\) ,因此时间复杂度为 \(O(\sqrt p)\)

特殊注意:\(a \equiv 0 \pmod p\) 时当且仅当 \(b \equiv 0 \pmod p\) 时有解( \(x=1\) ),否则无解。

code
#include <map>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define int long long
#define Reg register
using namespace std;
int b,p,n;
map<int,int> vis;
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    return s*w;
}
inline int qpow(int A,int B){
    int Ans=1;
    while(B){
        if(B&1) Ans=Ans*A%p;
        A=A*A%p;
        B>>=1;
    }
    return Ans;
}
inline int BSGS(){
    n%=p;
    int t=sqrt(p)+1;
    for(Reg int i=0;i<t;++i) vis[n]=i,n=n*b%p;
    int v=qpow(b,t),f=1;
    if(!v){
        if(!n) printf("0\n");
        else printf("no solution\n");
        exit(0);
    }
    for(Reg int i=0;i<=t;++i){
        int j=(vis.find(f)==vis.end())?-1:vis[f];
        if(j>=0&&i*t-j>=0) return i*t-j;
        f=f*v%p;
    }
    printf("no solution\n");
    exit(0);
    return 0;
}
signed main(){
    p=read(),b=read(),n=read();
    printf("%lld\n",BSGS());
    return 0;
}

实际上以上流程可以适用于 \(\gcd(a,p)=1\) 的情况。

但如果 \(\gcd(a,p)\ne 1\) 呢?

拓展BSGS

题意:

给定正整数 \(a,b,p\),求 \(a^x\equiv b \pmod p\) 的最小非负整数解,若无解则输出 no solution

多组询问,保证 \(\sum \sqrt p \le 5\times 10^6\)

$ 1\le a,b,p \le 10^9$

思路:

发现 \(a,p\) 不一定互质了(悲

容易知道普通 \(\operatorname{BSGS}\) 能够得出正解得原因是存在 \(a^j\)\(p\) 的逆元,而现在这个条件已经不被满足。

考虑将题意转化成 \(\gcd(a,p)=1\) 的情况。假设我们已经求出来了 \(x\)

\[a^x \equiv b \pmod p \]

上式可以转化成二元不定方程的形式:

\[ a^xX-pY=b \]

拆一个 \(a\) 出来:

\[ a^{x-1} aX-pY=b \]

\(d=gcd(a,p)\),根据翡蜀定理,只有 \(d| b\) 时方程才有解,于是我们直接给两边除以 \(d\)

\[ a^{x-1} \frac{a}{d}X-\frac{p}{d}Y=\frac{b}{d} \]

这么处理下去,直到 \(gcd(a,b)=1\)

\[a^{x-c} \frac{a^c}{\prod_{i=1}^c d_i} X- \frac{p}{\prod_{i=1}^c d_i} Y = \frac{b}{\prod_{i=1}^c d_i} \]

\(g=\frac{a^c}{\prod_{i=1}^c d_i},p'=\frac{p}{\prod_{i=1}^c d_i},b'=\frac{b}{\prod_{i=1}^c d_i}\),此时我们应该求以下同余方程的解:

\[a^{x-c} g\equiv b' \pmod {p'} \]

因为 \(gcd(a,p')=1\) ,所以能使用普通 \(\operatorname{BSGS}\) 求解,最后的答案为 \(i\times t-j+c\)

由于在 \(gcd(a,p)\ne 1\) 时, \(gcd(a,p)\ge 2\) ,可知 \(c\)\(O(\log p)\) 级别的,单次询问的复杂度为 \(O(\sqrt p+\log p)\)

细节比较多。

code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#define Reg register
#define ll long long
const int maxn=5100000;
int a,b,mod;
gp_hash_table<ll,ll> vis;
//unorder_map被卡了(悲,不想手打哈希表的可以写这个
inline ll read(){
    ll s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    return s*w;
}
inline ll qpow(ll A,ll B,ll p){
    ll Ans=1;
    while(B){
        if(B&1) Ans=Ans*A%p;
        A=A*A%p;
        B>>=1;
    }
    return Ans;
}
inline ll gcd(ll A,ll B){
    return B?gcd(B,A%B):A;
}
inline void Nosolu(){
    printf("No Solution\n");
}
inline void EXBSGS(){
    a%=mod,b%=mod;
    if(b==1||mod==1) return printf("0\n"),void();
    if(!a){
        if(b) return Nosolu(),void(); 
        return printf("1\n"),void();
    }
    int c=0,p=mod,g=1;
    for(Reg int d=gcd(a,p);d!=1;d=gcd(a,p)){
        if(b%d) return Nosolu(),void();
        ++c,p/=d,b/=d,g=1ll*g*(a/d)%p;
        if(b==g) return printf("%d\n",c),void(); 
    }
    vis.clear();
    if(p==1) return printf("%d\n",c),void();
    int t=sqrt(p)+1;
    for(Reg int i=0;i<t;++i) vis[b]=i,b=1ll*b*a%p;
    int At=qpow(a,t,p),f=g;
    for(Reg int i=1;i<=t;++i){
        f=1ll*f*At%p;
        ll j=(vis.find(f)==vis.end())?-1:vis[f];
        if(j>=0&&i*t-j>=0) return printf("%lld\n",i*t-j+c),void();
    }
    Nosolu();
}
int main(){
    a=read(),mod=read(),b=read();
    while(!(a==0&&b==0&&mod==0)){ 
        EXBSGS();
        a=read(),mod=read(),b=read();
    }
    return 0;
}

posted @ 2023-02-07 20:13  Broken_Eclipse  阅读(39)  评论(0编辑  收藏  举报

Loading