原根&离散对数简单总结
原根&离散对数
1.原根
1.定义:
定义\(Ord_m(a)\)为使得\(a^d\equiv1\;(mod\;m)\)成立的最小的d(其中a和m互质)
由欧拉定理可知:
\(Ord\le\Phi(m)\)
当\(Ord_m(a)=\Phi(m)时,称a是模m意义下m的一个原根\)(记住原根是a,不是d!)
2.原根的性质:
1.具有原根的数字仅有以下几种形式:\(2,4,p^n,2·p^n\)(p是奇质数)
2.一个数的最小原根的大小不超过 \(m^{\frac14}\)
3.若g是m的一个原根,那么\(g^d\)是m的原根的充分必要条件是\(gcd(d,\Phi(m))=1\),
由此可推知一个数的原根个数为\(\Phi(\Phi(m))\)个
3.求解原根的基本步骤:
- 判断一个数是否有原根。(通过性质1,枚举质数即可)
- 求得最小原根。(通过性质2,依次枚举\(2~m^{\frac14}\)判断即可)
- 求出所有原根。(通过性质3,枚举次数d即可)
代码实现:
1.筛出质数并进行第一步(顺便把欧拉函数也筛出来):
void get_prime()
{
is_pri[1]=1;
for(register int i=2;i<N;i++){
if(!is_pri[i]) Prime[++tot]=i,phi[i]=i-1;
for(register int j=1;j<=tot;j++){
if(1ll*i*Prime[j]>=N) break;
register int res=i*Prime[j];
is_pri[res]=1;
if(i%Prime[j]==0){
phi[res]=phi[i]*Prime[j];
break;
}
phi[res]=phi[i]*phi[Prime[j]];
}
}
}
bool judge(int m)//判断原根有无
{
if(m==1) return 0;
if(m==2||m==4) return 1;
if((m&1)==0) m>>=1;if((m&1)==0) return 0;
for(int i=2;i<=tot&&(1ll*Prime[i]*Prime[i]<=m);i++)
{
if(m%Prime[i]!=0) continue;
while(m%Prime[i]==0) m/=Prime[i];
if(m==1) return 1;
return 0;
}
return m;
//这里要return m, 由于Prime[i]*Prime[i]>m即退出的影响
}
2.找出最小原根:
for(int i=2;1ll*i*i<=phi[m];i++){
//筛出phi的约数,用于check
if(phi[m]%i==0){
st[++top]=i;
if(i*i!=phi[m]) st[++top]=phi[m]/i;
}
}
int g;
for(g=2;g<=100;g++)//枚举最小原根
{
if(check(g,m)) break;
}
bool check(int x,int m)
{
if(Pow(x,phi[m],m)!=1) return 0;
for(int i=1;i<=top;i++)if(Pow(x,st[i],m)==1) return 0;
return 1;
}
int Pow(int x,int n,int mod)
{
int ans=1;
while(n){
if(n&1) ans=(1ll*ans*x)%mod;
x=(1ll*x*x)%mod;
n>>=1;
}
return ans;
}
3.找出所有原根
int cnt=0;
register int res=1;
for(register int i=1;i<=phi[m];i++)
//由欧拉定理,只用枚举到g^phi[m]
{
if(cnt==phi[phi[m]]) break;//个数限制
res=(1ll*res*g)%m;
if(gcd(i,phi[m])!=1) continue;
ans[++cnt]=res;
}
sort(ans+1,ans+1+cnt);//由于取了模,要sort
for(register int i=1;i<=cnt;i++)
{
if(i>1) putchar(' ');
printf("%d",ans[i]);
}
puts("");
2.离散对数
1.定义
普通对数:
若$$a^x=b$$则称\(x\)是\(b\)以\(a\)为底的对数,记做\(x=log_ab\)
离散对数就是把这个放在了模意义下,即求解方程:
\]
2.求解方法
方法1-暴力法: 猜测这东西有循环节,直接暴力枚举x,哈希什么的判循环,循环则无解
方法2-带有数学知识的暴力:
由欧拉定理:\(a^{\phi (p)} \equiv 1\ (mod\ p)\)
于是我们只需要把\(x\)从\(0\)枚举到\(\phi(p)-1\)就可以了
方法3-正解
BSGS 大步小步算法
这是一种Meet in the middle思想的应用,普通的BSGS只适用于a,p互质的情况,所以我们先讨论a,b互质的情况
BSGS:
我们既然是枚举,那么就有折半枚举这种思想,这里也类似,对于方程:
\]
我们可以把\(x\)表示为\(t*u-v\) 的形式,保证\(0<v<u\),然后把左边的负的乘过去,就是:
\]
相当于是把一个数给除了过去,所以只能用于a,p互质
于是我们发现如果先算出一系列的v取什么值的时候右边的值存入哈希表,然后枚举左边的t,就可以在哈希表中直接查询出解了,并且第一个找到的一定是最小的
所以显然u取\(\sqrt {p-1}\)的时候比较优秀
时间复杂度\(O(\sqrt{p})\),如果手写哈希的话,用map就多一个log
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<queue>
using namespace std;
const int N=1e5+1;
const int mod=1e6+3;
typedef long long ll;
struct hashlist{
int key[N],next[N],head[mod<<1];
int cnt;int vb[N];
inline void insert(int x,int b){
register int ret=x%mod,p;
for(p=head[ret];p;p=next[p]){
if(key[p]==x) return void(vb[p]=max(vb[p],b));
}
++cnt;vb[cnt]=b;key[cnt]=x;next[cnt]=head[ret];head[ret]=cnt;
return;
}
inline int find(int x){
register int ret=x%mod,p;
for(p=head[ret];p;p=next[p]) if(key[p]==x) return vb[p];
return -1;
}
}Hash;
int p,a,b,sz;
inline int fpow(int x,int k){
register int res=1;
while(k){
if(k&1) res=1ll*res*x%p;
x=1ll*x*x%p;
k>>=1;
}
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BSGS.in","r",stdin);
freopen("BSGS.out","w",stdout);
#endif
scanf("%d %d %d",&p,&a,&b);//a^x=b (mod p)
sz=ceil(sqrt(p-1));//向上取整,保证不会漏解
register int ans=-1;
if(b==1) return puts("0"),0;
for(register int i=0;i<sz;++i) Hash.insert(b,i),b=1ll*b*a%p;
a=fpow(a,sz);
register int res=a;
for(register int i=1;i<=sz;++i){
register int q=Hash.find(res);
if(q!=-1) {ans=i*sz-q;break;}
res=1ll*res*a%p;
}
if(ans==-1) puts("no solution");
else printf("%d\n",ans);
return 0;
}
扩展BSGS:
然后讨论a,p不是质数的情况
唯一的问题是我们不能直接把一个数给除过去,因为a,p不互质的话a是没有逆元的
我们记\(gcd(a,p)=d\),根据同余的一个性质:
若 \(a*c \equiv b*c \ (mod \ p)\),\(gcd(c,p)=d\),则\(a\equiv b \ (mod\ \frac{p}{d})\)
于是我们可以通过这个性质不断通过提取左边的一个a去把右边的p给弄成a,p是互质的情况,记录一下我们提取了多少个,最后加入答案就行了,如果提取过程中\(b\)不能被\(d\)整除就无解
记得如果在提取过程中已经出解了就要判掉
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<queue>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e5+1;
const int mod=1e6+3;
typedef long long ll;
inline int fpow(int x,int k,int MO){
register int res=1;
while(k){
if(k&1) res=1ll*res*x%MO;
x=1ll*x*x%MO;
k>>=1;
}
return res;
}
namespace BSGS{
struct hashlist{
int key[N],next[N],head[mod];
int cnt;int vb[N];
void clear(){Set(key,0);Set(next,0);Set(head,0);Set(vb,0);cnt=0;}
inline void insert(int x,int b){
register int ret=x%mod,p;
for(p=head[ret];p;p=next[p]){
if(key[p]==x) return void(vb[p]=max(vb[p],b));
}
++cnt;vb[cnt]=b;key[cnt]=x;next[cnt]=head[ret];head[ret]=cnt;
return;
}
inline int find(int x){
register int ret=x%mod,p;
for(p=head[ret];p;p=next[p]) if(key[p]==x) return vb[p];
return -1;
}
}Hash;
inline int solve(int p,int a,int b,int d,int k){
Hash.clear();int sz=ceil(sqrt(p-1));register int ans=-1;
for(register int i=0;i<sz;++i){Hash.insert(b,i);b=1ll*b*a%p;}
a=fpow(a,sz,p);
for(register int i=1;i<=sz;++i){
d=1ll*d*a%p;
register int q=Hash.find(d);
if(q!=-1) {
ans=i*sz-q+k;break;
}
}
return ans;
}
}
int gcd(int a,int b){return (b? gcd(b,a%b):a);}
inline void EXBSGS(int p,int a,int b){
a%=p;b%=p;
if(b==1) return void(puts("0"));
register int g=gcd(a,p),d=1,k=0;
while(((g=gcd(a,p))!=1)){//不互质就继续消
if(b%g) return void(puts("no solution"));//gcd不整除 b 则无解
++k;b/=g,p/=g;d=1ll*d*(a/g)%p;//记录提取数量
if(b==d) return void(printf("%d\n",k));//答案在消的过程中产生的情况
}
register int ans=BSGS::solve(p,a,b,d,k);
if(ans==-1) return void(puts("no solution"));
printf("%d\n",ans);
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("EXBSGS.in","r",stdin);
freopen("EXBSGS.out","w",stdout);
#endif
register int p,a,b;
while(scanf("%d %d %d",&p,&a,&b)!=EOF) EXBSGS(p,a,b);
}
THE END
updated on 2018.8.16
原根&离散对数简单总结的更多相关文章
- CF1106F Lunar New Year and a Recursive Sequence(矩阵快速幂+bsgs+exgcd)
题面 传送门 前置芝士 \(BSGS\) 什么?你不会\(BSGS\)?百度啊 原根 对于素数\(p\)和自然数\(a\),如果满足\(a^x\equiv 1\pmod{p}\)的最小的\(x\)为\ ...
- 题解 loj3050 「十二省联考 2019」骗分过样例
CASE \(1\sim 3\) \(n\)组测试数据,每次输入一个数\(x\),求\(19^x\). 测试点\(1\),\(x=0,1,\dots n-1\),可以直接递推. 测试点\(2\)要开l ...
- HDU3930(离散对数与原根)
题目:Broot 题意:给出k,m,newx的值,求方程x^k(mod m)=newx的解,其中m为素数. 解法步骤: (1)先暴力求m的原根g (2)大步小步求g^t1(mod m)=newx (3 ...
- 证明与计算(2): 离散对数问题(Discrete logarithm Problem, DLP)
离散对数问题,英文是Discrete logarithm Problem,有时候简写为Discrete log,该问题是十几个开放数学问题(Open Problems in Mathematics, ...
- OI多项式 简单学习笔记
咕咕咕 先开个坑(其实是存模板来了) 一些特别简单的前置东西qwq 复数的计算 复数相加:向量相加,复数相乘.复数相乘:模长相乘,旋转量相加(就是复平面坐标轴逆时针旋转的角度) (当然也可以直接使用c ...
- HDU5478 原根求解
看别人做的很简单我也不知道是怎么写出来的 自己拿到这道题的想法就是模为素数,那必然有原根r ,将a看做r^a , b看做r^b那么只要求出幂a,b就能得到所求值a,b 自己慢慢化简就会发现可以抵消n然 ...
- 土法炼钢:怎么实现一个简单的B+Tree In-Disk
1. 写在前面 说起B+树,大家应该都很熟悉.B+树是一种平衡的多路搜索树,广泛在操作系统和数据库系统用作索引.相比于内存的存取速度,磁盘I/O存取的开销要高上几个数量级.而将B+树用作索引时,它可以 ...
- BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1017 Solved: 466[Submit][Statu ...
- Root(hdu5777+扩展欧几里得+原根)2015 Multi-University Training Contest 7
Root Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Su ...
随机推荐
- mysql 5.6 后热数据的加载
mysql 5.6 后热数据的加载 转自:http://blog.itpub.net/20892230/viewspace-2127469/ 故障现象:在数据库重启后,碰巧遇到业务高峰期,连接数满,导 ...
- mybatis-sql执行流程源码分析
1. SqlSessionFactory 与 SqlSession. 通过前面的章节对于mybatis 的介绍及使用,大家都能体会到SqlSession的重要性了吧, 没错,从表面上来看,咱们都是通过 ...
- <<C++ Primer>> 第 7 章 类
术语表 第 7 章 类 抽象数据类型(abstract data type): 封装(隐藏)了实现细节的数据结构. 访问说明符(access specifier): 包括关键字 public 和 ...
- 虚拟机上安装Linux系统之ubuntu
以前自己在虚拟机上安装过几回Linux系统,有centos.ubuntu,不过都没来得及写一个安装教程,今天正好需要重新安装一下,就分享一个安装ubuntu的详细教程 安装前准备: VMWare虚拟机 ...
- Codeforces 1190C. Tokitsukaze and Duel
传送门 注意到后手可以模仿先手的操作,那么如果一回合之内没法决定胜负则一定 $\text{once again!}$ 考虑如何判断一回合内能否决定胜负 首先如果最左边和最右的 $0$ 或 $1$ 距离 ...
- Codeforces 1190B. Tokitsukaze, CSL and Stone Game
传送门 不妨把每一堆按照石头数量从小到大排序 注意到每次只能拿一个石头,那么不论何时每堆石头的排名都是一样的 那么最终所有堆的状态一定就是 $0,1,2,...,n-1$,现在每一堆最终的石头数量都确 ...
- CentOS7部署Tomcat服务器
1. 软件 存放路径:/usr/local/src apache-tomcat-9.0.22.tar.gz openjdk-12_linux-x64_bin.tar.gz 2.事先配置 启动后关闭防火 ...
- Http的简单介绍
之前写过一篇使用HttpListener作为简单的HTTP服务器,后面实际项目中就用到了,测试发现,在Win7下如果不是以管理员权限运行程序,使用HttpListener是会出错了. 所以就很好奇HT ...
- 第四篇 jQuery中的事件与应用
4.1 事件中的冒泡现象,ready()方法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" & ...
- [转载]MySQL的存储引擎
[转载]MySQL的存储引擎 来源:https://www.cnblogs.com/lina1006/archive/2011/04/29/2032894.html 其实这是个只有在MySQL中才存在 ...