首页 > 其他分享 >阶 原根 离散对数

阶 原根 离散对数

时间:2023-06-05 20:12:33浏览次数:37  
标签:原根 int varphi 离散 pmod 对数

阶 原根 离散对数

定义

\(a\mod p\) 的阶是 \(a^e\equiv1\pmod p\) 的最小指数 \(e\)

符号语言: \(\delta_p(a)\) 代表 \(a\) 在 \(\mod p\) 的意义下的最小指数 \(e\) 使\(a^e\equiv1\pmod p\)

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

\[\delta_5(1) = 1~~~\delta_7(4) = 3~~~\delta_{11}(9) = 5 \]

原根

定义

\[a^{q}\not\equiv 1\pmod m~~~~~~~~~q,a\in[1,\varphi(m))\cup Z \]

满足上述则 \(a\) 是 \(\mod m\) 意义下的原根

最小原根 \(g\)

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

找出一个可能是原根的数,我们从 \([1-\varphi(n))\) 枚举每个 \(k\) 判断 \(now^k\equiv1\pmod m\) 是否成立

如果全都不同余 \(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,\varphi(m))=1条件下,(g^{k})也是模m意义下的原根 \]

我们考虑当 \(gcd(k,\varphi(m))=g\) 的时候 \({g^{k}}^{\frac {\varphi(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,p^k,2×p^k,其中 p 为奇素数,k 为正整数。\)

证明详见

原根

原根数量

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

\[在gcd(k,\varphi(m))=1条件下,(g^{k})也是模m意义下的原根~~~k\in[1,\varphi(m)) \]

有多少个 \(k\) 满足:\(k\in[1,\varphi(m))~~~gcd(k,\varphi(m))=1\)

其实就是 \(\varphi(\varphi(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\),使得 \(g^x\equiv b \pmod {p}\) 则称 \(x\) 为 \(b\) 的离散对数,记作

\(\log_g(b)\)

性质

1.当 \(p\) 为质数时,\(∀i ∈ [0, p − 1]\) 在 \([0, p − 1]\) 范围内都有唯一对应的离散对数。

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

利用离散对数可以将模 \(p\) 意义下的 \(xy\) 转化为 $ g^{\log_g
(x)+\log_g
(y)}$

BSGS

题目描述:

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

根据性质1,在 \(x\in[1,m]\implies b\in[1,m]\)

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

我们考虑更优秀的枚举:

\(设x=A\times\sqrt m-B(A,B\in[1,{\sqrt m}])\)

可以发现现在依旧 \(x\in[1,m]\)

转换一下

\[a^{A\times\sqrt m-B}\equiv b\pmod m\implies a^{A\times\sqrt m}\equiv b\times a^B\pmod m \]

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

用map记录所有 \(b \times a^B~ mod~m\)

再枚举A算出 \(a^{A*\sqrt m}~mod~m\) 在map找找有没有对应的

标签:原根,int,varphi,离散,pmod,对数
From: https://www.cnblogs.com/hfjh/p/17458808.html

相关文章

  • Leetcode 2460. 对数组执行操作
    题目:给你一个下标从0开始的数组nums,数组大小为n,且由非负整数组成。你需要对数组执行n-1步操作,其中第i步操作(从0开始计数)要求对nums中第i个元素执行下述指令:如果nums[i]==nums[i+1],则nums[i]的值变成原来的2倍,nums[i+1]的值变成0。否则,跳过......
  • 离散数学图论部分总结
    图论内容总结前言:图论这一部分内容可谓离散数学的点睛之笔,离散数学很多堆砌的概念在这章似乎都活过来了(可能是因为我刷算法题的原因),概念之间的联系更加的紧密。学完图论部分我感觉里面很多的知识点都非常重要,比如顶点度数,握手定理,树,而考点的话除了这些,还有求欧拉回路,最短路径问......
  • 离散数学代数系统部分总结
    代数系统部分总结前言:本节的重点在于掌握二元关系的相关概念,群的相关概念,主要的题型有计算运算表中的幺元、零元,证明某二元运算符合结合律,证明某代数系统为群,判定子群等。目录:二元运算及其性质代数系统群与子群二元运算及其性质设S为集合,函数f:SxS→S称为S上的二元运......
  • 离散数学-数理逻辑
    《离散数学》是计算机专业的一门十分重要的专业基础课。离散数学作为有力的数学工具对计算机的发展、计算机研究起着重大的作用。目前,计算机科学中普通采用离散数学中的一些基本概念、基本思想和基本方法。通过本课程的学习,掌握数理逻辑、集合论、代数和图论等近代数学分支的最基本......
  • 【C语言】动态内存管理函数的 深度解析 #是不是对数组不能变大变小而烦恼呢?学会动态内
    前言动态内存管理函数可以说很好用,但是有些小危险。所谓动态内存分配,就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求......
  • 【web 开发】PHP8中对数组操作的新变化
    自动创建元素的顺序改变在PHP8中,引用赋值时,自动创建的数组元素或者对象属性的顺序和PHP7版本相比发生了变化,下面我们通过例子来体验下变化在哪里.<?php$array=[];$array['a']=&$array['b'];$array['b']=1;echo"\n";var_dump($array);?>执行结果如下:这个结果是PHP8......
  • EasyGBS针对数据库删除级联数据后的无效数据进行的优化
    EasyGBS国标视频云服务可支持通过国标GB28181协议将设备接入,实现视频的实时监控直播、录像、语音对讲、云存储、告警、级联等功能,同时也支持将接入的视频流进行全终端、全平台分发,分发的视频流包括RTSP、RTMP、FLV、HLS、WebRTC等格式。同时EasyGBS平台也支持海康Ehome协议及SDK等......
  • Python字典:强大的键值对数据结构
    在Python中,字典是一种多功能和强大的数据结构,它允许我们以键值对的形式存储和操作数据。字典在其他编程语言中也被称为关联数组或哈希映射,它提供了一种高效的方式来根据键检索和更新值。在本文中,我们将探讨Python中的字典概念,并了解如何有效地使用它们。Python中的字典是无序的键......
  • POJ2528 线段树+离散化+hash(成段更新)
    题目:Mayor'sposters 题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报思路:这题数据范围很大,直接搞超时+超内存,需要离散化:离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012]我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][201......
  • 小米 Online Judge TCO 预选赛 Rectangle [离散化+二维前缀和]
    题目链接:https://code.mi.com/problem/list/view?id=151&cid=13 解题思路:首先将x轴和y轴坐标离散化,然后就可以用二维前缀和求得每个格子被覆盖了几次,然后就可以求出每个格子的贡献,最后将总的贡献和乘以总的方案数的逆元即可。#include<bits/stdc++.h>#definexfirst#define......