阶 原根 离散对数

阶 原根 离散对数

定义

amodp 的阶是 ae1(modp) 的最小指数 e

符号语言: δp(a) 代表 amodp 的意义下的最小指数 e 使ae1(modp)

根据这个表格,我们可以举出一些例子

δ5(1)=1   δ7(4)=3   δ11(9)=5

原根

定义

aq1(modm)         q,a[1,φ(m))Z

满足上述则 amodm 意义下的原根

最小原根 g

我们枚举,如果 gcd(now,n)1 那一定不是原根

找出一个可能是原根的数,我们从 [1φ(n)) 枚举每个 k 判断 nowk1(modm) 是否成立

如果全都不同余 1 ,那么就找到了 g ,可以容易的找出其他原根:

    while(++g){
        int now=1,bj=0;
        if(gcd(g,n)!=1) continue;
        for(int j=1;j<phi[n];j++){
            now=now*g%n;
            if(now==1){
                bj=1;
                break;
            }
        }
        if(bj==1) continue;
        else if(bj==0){
            break;
        }
    }

找出其他原根

我们认为 g 是最小的原根

寻找方法:

gcd(k,φ(m))=1,(gk)m

我们考虑当 gcd(k,φ(m))=g 的时候 gkφ(m)k 会同余 1

代码

    int now=g;
    ans[++cnt]=g;
    for(int j=2;j<phi[n];j++){
        now=now*g%n;
        if(gcd(j,phi[n])!=1) continue;
        ans[++cnt]=now;
    }

有无原根

这些数有原根

2,4,pk,2×pkpk

证明详见

原根

原根数量

我们在前面可以知道,当求出一个g(最小原根),

gcd(k,φ(m))=1,(gk)m   k[1,φ(m))

有多少个 k 满足:k[1,φ(m))   gcd(k,φ(m))=1

其实就是 φ(φ(m))

总代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int phi[N],prim[N],v[N],vis[N],tot=0,ans[N],cnt=0;
int t,n,d;
void pre(){
    phi[1]=1;
    for(int i=2;i<=N-1;i++){
        if(!v[i]){
            prim[++tot]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=tot&&prim[j]*i<=N-10;j++){
            v[i*prim[j]]=1;
            if(i%prim[j]==0){
                phi[i*prim[j]]=phi[i]*prim[j];
                break;    
            }else{
                phi[i*prim[j]]=phi[i]*phi[prim[j]];
            } 
        }
    }
    vis[2]=1;
    vis[4]=1;
    for(int i=2;i<=tot;i++){
        for(long long j=1;j<=N;j=j*prim[i]){
            if(j>N-10){
                break;    
            }
            vis[j]=1;
            if(2*j<=N-1) vis[2*j]=1;
        }
    }
}//预处理phi和prime
int gcd(int x,int y){
    if(y==0) return x;
    return gcd(y,x%y);
}
void input(){
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        cnt=0;
        memset(ans,0,sizeof(ans));
        scanf("%d%d",&n,&d);
        if(!vis[n]){
            printf("0\n\n");
            continue;    
        }
        int g=0;
        while(++g){
            int now=1,bj=0;
            if(gcd(g,n)!=1) continue;
            for(int j=1;j<phi[n];j++){
                now=now*g%n;
                if(now==1){
                    bj=1;
                    break;
                }
            }
            if(bj==1) continue;
            else if(bj==0){
                break;
            }
        }
        int now=g;
        ans[++cnt]=g;
        for(int j=2;j<phi[n];j++){
            now=now*g%n;
            if(gcd(j,phi[n])!=1) continue;
            ans[++cnt]=now;
        }
        sort(ans+1,ans+1+cnt);
        printf("%d\n",phi[phi[n]]);
        for(int j=1;j<=phi[phi[n]]/d;j++){
            printf("%d ",ans[j*d]);
        }
        printf("\n");

    }
}
int main(){
//     freopen("1.txt","w",stdout);
    pre();
    input();

    return 0;
}

离散对数

就是对数的定义,只不过在模意义下

定义

对于正整数 p , p 的原根 g ,整数 b,使得 gxb(modp) 则称 xb 的离散对数,记作

logg(b)

性质

1.当 p 为质数时,i[0,p1][0,p1] 范围内都有唯一对应的离散对数。

2.当 p 为奇质数的幂时,p 的倍数不存在离散对数,通常需要特殊处理。2pk 也类似。

利用离散对数可以将模 p 意义下的 xy 转化为 glogg(x)+logg(y)

BSGS

题目描述:

已知 a,b,p,求模 p 意义下 x=loga(b) ,保证 p 为质数 。

根据性质1,在 x[1,m]b[1,m]

我们枚举 x ,可以得到答案,但时间复杂度不能接受

我们考虑更优秀的枚举:

x=A×mB(A,B[1,m])

可以发现现在依旧 x[1,m]

转换一下

aA×mBb(modm)aA×mb×aB(modm)

发现现在只有两个未知数A,B我们可以先枚举一次B预处理

用map记录所有 b×aB mod m

再枚举A算出 aAm mod m 在map找找有没有对应的

posted @   He_Zi  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示