首页 > 其他分享 >原根与BSGS

原根与BSGS

时间:2023-02-07 20:24:01浏览次数:32  
标签:return gcd 原根 pmod int BSGS equiv

我是鸽子。

$ \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;
}

标签:return,gcd,原根,pmod,int,BSGS,equiv
From: https://www.cnblogs.com/Broken-Eclipse/p/17096815.html

相关文章

  • BSGS
    简介BSGS(baby-stepgiant-step),即大步小步算法,常用于求解离散对数问题。形式化地说,该算法可以在\(O(\sqrt{p})\)的时间内求解\(a^x\equivb\pmodp\)(\(a\perpp\))方......
  • 【学习笔记】BSGS与原根学习笔记
    参考资料:OI-Wiki\(\text{BSGS}\)(\(\text{Big-Step-Giant-Step}\))最基本的线性同余方程:\[ax\equivb\pmodp\]可以转化成不定方程:\[ax+py=b\]使用扩展欧几里得算法......
  • 算法学习笔记(11): 原根
    原根此文相对困难,请读者酌情食用在定义原根之前,我们先定义其他的一点东西阶通俗一点来说,对于\(a\)在模\(p\)意义下的阶就是\(a^x\equiv1\pmodp\)的最小正......
  • 求原根
    原根:原根定义:\[a^{q}\not\equiv1\pmodm~~~~~~~~~q,a\in[1,\varphi(m))\cupZ\]满足上述则a是模m意义下的原根如何找出最小的原根g呢?我们从1开始枚举now,如果\(gcd(no......
  • 扩展大步小步法(exBSGS)
    从昨天调到今天,刚调过总结一下。exBSGS是解决\(a^{l}\equivb(\modp)(\gcd(a,p)\ne1)\)求最小非负整数\(l\)的问题。\(a^{l-1}\timesa\equivb(\modp)\)$a^{l-1}\ti......
  • 算法学习笔记(10): BSGS算法及其扩展算法
    BSGS算法及其扩展算法BSGS算法所谓BabyStep,GiantStep算法,也就是暴力算法的优化用于求出已知\(a,b,p,p为质数\)时\(a^x\equivb\pmodp\)的一个最小正整......
  • 2023.1.16[模板]BSGS/exBSGS
    2023.1.16[模板]BSGS/exBSGS全称BoyStepGirlStep给定一个质数p,以及一个整数a,一个整数b,现在要求你计算一个最小的非负整数l,满足\(a^x\equivb(modp)\)算法......
  • BSGS&exBSGS
    BSGS即baby(boy)stepgiant(girl)step算法,用于处理\(a^x\equivb(mod\p)\),给\(a,b,p(a,p互质)\)求所有的\(x\)的问题中本质上有一点点像二分搜索的意味在里面算法......
  • 大步小步算法(BSGS)
    BSGS是解决\(a^{l}\equivb(\modp)\)已知\(a\)、\(b\)、\(p\)的情况下求最小的非负整数\(l\)的算法。设$m=\left\lceil\sqrt{p}\right\rceil$,\(l=x\timesm-y(0\l......
  • BSGS
    BSGS(大步小步法)BSGS可以在\(O(\sqrt{p})\)的时间内求解满足\(a^x\equivb\pmodp\)的\(x\),其中\(a\perpp,x>0\)。实现原理:尝试进行和整除分块异曲同工的......