思路来源

https://baike.baidu.com/item/%E5%8E%9F%E6%A0%B9/8103534?fr=aladdin

https://blog.csdn.net/zoro_n/article/details/78200742

https://www.cnblogs.com/Dance-Of-Faith/p/9905786.html

《数学的思维方式与创新》 丘维声著 北京大学出版社 2011年3月第一版

看到的一些和原根相关的比较好玩的东西

http://www.sohu.com/a/226489349_100125672

http://www.sohu.com/a/214094143_815436

概念

提示:如果下述出现一些符号不明白,可下翻到参考概念及预备知识部分

设m是正整数,a是整数,若a模m的阶等于φ(m)(即|a|==φ(m)),则称a为模m的一个原根。

*群的元素的阶

性质

①如果正整数(a,m) = 1和正整数 d 满足≡1(mod m),则 d 整除 φ(m)。

因此|a|整除φ(m)。当a= 3时,我们仅需要验证 3 的 1 、2、3 和 6 次方模 7 的余数即可。

证明:

显然,由欧拉定理,,结合下述命题2得证

从这个思路出发,这为寻找模m意义下的最小原根提供了方法论,

至少可以帮我们快速判定一个数不是原根,

性质1的方法论:

如果对于所有的素因子prime[i],模m均不为1,则|a|==φ(m),a为原根

证明:

上述证明引自博客https://www.cnblogs.com/Dance-Of-Faith/p/9905786.html

一点小说明,对于引理2,我们可以递归进行这个操作,,从而继续往小求,直至

由于g是的一个约数,g<,则g至少比少一个素因子,那么一定是g的倍数,则得证

,……,模 m 两两不同余,因此当a是模m的原根时,,……,构成模 m 的简化剩余系。

简化剩余系,1到m-1中所有与m互素的数的集合。

,……,这些数只能是可逆元,因此是可逆元的集合,不可能出现零因子,故均与m互素。

③模m有原根的充要条件是m= 1,2,4,,,其中p是奇质数,n是任意正整数。

常用:2的原根是1;4的原根是3;998244353的原根是3;1e9+7的原根是5

该证明由高斯在1801年完成,菜鸡表示不会证QAQ

④对正整数(a,m) = 1,如果 a 是模 m 的原根,那么 a 是整数模n乘法群(即加法群 Z/mZ的可逆元,也就是所有与 m 互素的正整数构成的等价类构成的乘法群)Zn的一个生成元。由于Zn有 φ(m)个元素,而它的生成元的个数就是它的可逆元个数,即 φ(φ(m))个,因此当模m有原根时,它有φ(φ(m))个原根。

百度百科证明实在是看不懂,就是φ(φ(m))让我找到的这本书,更为详尽的证明

证明:

其实就是群论拉格朗日定理的一个结论,然而不写成这个命题3的形式我还是证不出来

*循环群与生成元

由群,若存在原根a,则

只需令,即有

知,为群的生成元,

k的个数即为的生成元的个数,

也为满足即与互质的数的个数,

这显然就是,即可逆元的个数,证毕

故原根个数为0或

一个数的最小原根的大小是O()的

详见百度百科最小正原根问题,由王元院士于1959年给出证明

⑥一个数n的全体原根乘积mod n==1

⑦一个数n的全体原根总和mod n==莫比乌斯函数μ(n-1)

⑧找到最小原根a后,所有与互素的数k,均为原根

其实求模m剩余循环群的所有生成元的过程,就是求m的原根的过程,

这二者是等价的,原根个数==2,即有两个生成元

例题

①poj1284 Primitive Roots 求奇素数p的原根个数

根据性质4,显然答案为

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=(1<<16)+10;
bool ok[maxn];
int prime[maxn],phi[maxn],cnt,n;
void sieve()
{phi[1]=1;for(ll i=2;i<maxn;++i){if(!ok[i]){prime[cnt++]=i;phi[i]=i-1;}for(int j=0;j<cnt;++j){if(i*prime[j]>=maxn)break;ok[i*prime[j]]=1;if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的因子 prime[j]的素因子项包含在i的素因子项里break; }else phi[i*prime[j]]=phi[i]*(prime[j]-1);//prime[j]与i互质 phi[i*prime[j]=phi[i]*phi[prime[j]]}}
}
int main()
{sieve();while(~scanf("%d",&n)){printf("%d\n",phi[phi[n]]);}return 0;
}

②hdu4992 Primitive Roots 求一个数n的所有原根(2<=n<1e6)

先根据性质3判是否存在原根,

若存在,再根据性质1的方法论从a==2开始往上找最小原根a,

找到原根a后,根据性质8,输出这些原根即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
bool ok[maxn];
vector<ll>cal,rt;
ll p,phi[maxn],prime[maxn],cnt;
ll modpow(ll x,ll n,ll mod)
{if(!n)return 1;ll p=modpow(x,n/2,mod),q=p*p%mod;if(n&1)q=q*x%mod;return q;
}
void sieve()
{ phi[1]=1;for(ll i=2;i<maxn;++i){if(!ok[i]){prime[cnt++]=i;phi[i]=i-1;}for(int j=0;j<cnt;++j){if(i*prime[j]>=maxn)break;ok[i*prime[j]]=1;if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的因子 prime[j]的素因子项包含在i的素因子项里break; }else phi[i*prime[j]]=phi[i]*(prime[j]-1);//prime[j]与i互质 phi[i*prime[j]=phi[i]*phi[prime[j]]}}
}
vector<ll>divisor(ll p)
{vector<ll>tmp;for(int i=0;i<cnt;++i){if(p%prime[i]==0){tmp.push_back(prime[i]);while(p%prime[i]==0)p/=prime[i];}}if(p>1)tmp.push_back(p);return tmp;
}
bool judge(ll p)
{if(p%2==0)p/=2;if(!ok[p])return 1; if(p%2==0)return 0;for(int i=1;i<cnt;++i)//得找奇素数 i=1很关键 {if(p%prime[i]==0){while(p%prime[i]==0)p/=prime[i];return p==1;}}return 0;
}
vector<ll>solve(ll p)
{vector<ll>t; if(p==2){t.push_back(1);return t;}if(p==4){t.push_back(3);return t;}if(!judge(p)){t.push_back(-1);return t;}ll root,now=1;//phi(p)cal=divisor(phi[p]);//phi(p)质因子 int len=cal.size();for(ll a=2;a<p;++a){if(modpow(a,phi[p],p)!=1)continue;bool flag=1;for(int i=0;i<len&&flag;++i){if(modpow(a,phi[p]/cal[i],p)==1)flag=0;}if(flag){root=a;break;}}for(ll i=1;i<phi[p];++i){now=(now*root)%p;if(__gcd(i,phi[p])==1)t.push_back(now);//algorithm库函数 }sort(t.begin(),t.end());ll sz=unique(t.begin(),t.end())-t.begin();//可能有重复 如26 t.resize(sz);return t;
}
int main()
{sieve();while(~scanf("%lld",&p)){rt=solve(p);int sz=rt.size();for(int i=0;i<sz;++i)printf("%lld%c",rt[i],i==sz-1?'\n':' ');}return 0;
}

③51nod1135 求最小原根(保证输入是一个3<=p<=1e9的奇素数)

和上面的代码大同小异,由于题意中p一定是素数,直接把phi[p]的地方都换成p-1即可

然后找到root之后就返回即可,不用找剩下的原根了

如果p不是素数的话,根据性质3,有原根的p最多有两个素因子,

在judge函数里记录一下,是素数还是不是素数,有哪几个素因子,

这样如果有原根的话就能顺便把phi(p)也给求了

URAL-1268 求最大原根(保证输入是一个3<=p<=65536的奇素数)

这题时限250ms,卡的比较紧,T了两发280ms

(1)把回答过的记忆化一下,毕竟不超过65536的奇素数估计也就4k,然而有6k多个询问

(2)是开vector预处理各个数的所有素因子,枚举素因子向vector里放,

每个数素因子不超过6个(2*3*5*7*11*13约等于3W),大概就是O(6*n)的叭,然后就卡过了

(3)从大到小枚举 比 从小到大找出原根再用gcd搞一个最大的原根 要快一些

(4)题目说是奇素了,把判是否存在原根的那些都杠掉

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=(1<<16)+10;
typedef long long ll;
bool ok[maxn];
vector<ll>cal,rt;
vector<ll>divisor[maxn];
ll p,phi[maxn],prime[maxn],ans[maxn],cnt;
ll modpow(ll x,ll n,ll mod)
{if(!n)return 1;ll p=modpow(x,n/2,mod),q=p*p%mod;if(n&1)q=q*x%mod;return q;
}
void sieve()
{ phi[1]=1;for(ll i=2;i<maxn;++i){if(!ok[i]){prime[cnt++]=i;phi[i]=i-1;ans[i]=-1;}for(int j=0;j<cnt;++j){if(i*prime[j]>=maxn)break;ok[i*prime[j]]=1;if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的因子 prime[j]的素因子项包含在i的素因子项里break; }else phi[i*prime[j]]=phi[i]*(prime[j]-1);//prime[j]与i互质 phi[i*prime[j]=phi[i]*phi[prime[j]]}}for(int k=0;k<cnt;++k){ll &i=prime[k];for(ll j=i;j<maxn;j+=i){divisor[j].push_back(i);           }}
}
bool judge(ll p)
{if(p%2==0)p/=2;if(!ok[p])return 1; if(p%2==0)return 0;for(int i=1;i<cnt;++i)//得找奇素数 i=1很关键 {if(p%prime[i]==0){while(p%prime[i]==0)p/=prime[i];return p==1;}}return 0;
}
ll solve(ll p)
{ if(p==2)return 1;if(p==4)return 3;//if(!judge(p))return -1; 保证是素数 ll root,now=1,final=0;//phi(p)cal=divisor[phi[p]];int len=cal.size();for(ll a=p-1;a>=2;--a){if(modpow(a,phi[p],p)!=1)continue;bool flag=1;for(int i=0;i<len&&flag;++i){if(modpow(a,phi[p]/cal[i],p)==1)flag=0;}if(flag){root=a;return root;}}
}
int main()
{int t;scanf("%d",&t);sieve(); while(t--){scanf("%lld",&p);if(ans[p]!=-1)printf("%lld\n",ans[p]);else printf("%lld\n",ans[p]=solve(p));}return 0;
}

应用

①如果模p意义下问一些数a[]的乘积的话,可以把a[]压缩成原根对应的值,变乘法为加法

如Codeforces Round #538 (Div. 2) F - Please, another Queries on Array?

如2018秦皇岛ccpc-camp Steins-Gate (原根+FFT)

②NTT,用最小原根的板子搞出原根来,才能做

③BSGS,当模的数p很大而需要被映射的值v很少的时候,

无法for循环一遍1-p-1,把值映射成原根对应的幂次,

用BSGS,对于每个v,是O(sqrt(p))的复杂度

参考概念及预备知识(上述证明符号不懂的看这里)

①模m剩余类(Zm)的概念

其实也就是离散学的一种同余关系,最普遍的那种模意义下的同余关系

②环的概念&&单位元的概念&&零因子的概念&&可逆元的概念

③可逆元与欧拉函数的关系

即ax==1(mod m)方程有解的充要条件是(a,m)==1

④可逆元的个数

的概念&&群的概念&&群的阶

注意到的加法不封闭,乘法封闭,因此只有一种运算,

两种运算+blahblah是环,一种运算+blahblah是群

为可逆元的集合,元素个数为可逆元个数即

原根(知识学习+板子总结+例题+应用)相关推荐

  1. 虚幻引擎的数学知识学习教程 Math for Unreal Engine (Early Preview)

    通过做真实世界的 Unreal Engine项目来学习数学 你会学到什么 理解游戏开发对数学的基本需求 将数学直接应用到用例中,而不是钻研理论(用我们的示例项目进行实践) 正确编辑短视频,节省您的时间 ...

  2. 三代测序知识学习----Sequel

    三代测序知识学习----Sequel (2017-03-26 22:38:34) 转载▼   分类: 三代 知识链接:http://www.pacb.com/blog/new-chemistry-so ...

  3. 计算机二级二叉树基础知识,2017年计算机二级公共基础知识学习教程:树与二叉树...

    (六)树与二叉树 1.树的基本概念 树是一种简单的非线性结构.在树结构中,数据元素之间有着明显的层次结构.在树的图形表示中,用直线连接两端的结点,上端点为前件,下端点为后件. 在树结构中,每一个结点只 ...

  4. c语言运算符ppt,C语言知识学习运算符.ppt

    C语言知识学习运算符.ppt 第三章,C语言运算符,回顾,变量和常量的含义 熟悉基本数据类型 - int.char.float 和 double 使用算术运算符 理解类型转换 熟练使用 scanf 和 ...

  5. 安全测试3_Web后端知识学习

    其实中间还应该学习下web服务和数据库的基础,对于web服务大家可以回家玩下tomcat或者wamp等东西,数据库的话大家掌握基本的增删该查就好了,另外最好掌握下数据库的内置函数,如:concat() ...

  6. Python 基础知识学习笔记——NumPy

    Python基础知识学习笔记--NumPy 与 matlab 优秀的矩阵运算类似,python 提供了 numpy 库,这对熟悉 matlab 的用户来说非常友好.向量.矩阵和多维数组是数值计算中必不 ...

  7. Python 基础知识学习笔记——OpenCV(1)

    Python 基础知识学习笔记--OpenCV(1) OpenCV是一个开源的跨平台计算机视觉和机器学习软件库,它轻量而且高效,被广泛的使用. 整理一下OpenCV学习笔记,以防忘记. 文章目录 Py ...

  8. Python基础知识学习笔记——Matplotlib绘图

    Python基础知识学习笔记--Matplotlib绘图 整理python笔记,以防忘记 文章目录 Python基础知识学习笔记--Matplotlib绘图 一.绘图和可视化 1.导入模块 2.一个简 ...

  9. python基础知识学习笔记(2)

    python基础知识学习笔记(2) 整理一下python基础知识,以防忘记 文章目录 python基础知识学习笔记(2) python简洁的一行代码 python简洁的一行代码 1.交换两个变量 # ...

最新文章

  1. Lucene.net: the main concepts
  2. c# 逆转数组元素的排序
  3. Oracle管理表空间和数据文件详解
  4. springmvc教程(2)
  5. Linux系统管理命令:date、free、ps、du、kill、uname
  6. Linux CenOS Python3 和 python2 共存
  7. 【技术分享】CSS 实现渐变色背景
  8. java5.0安装在x86,JDK 7U15在 Windows x86平台下的安装方法
  9. c语言case后面可以接表达式,c++ switch语句的使用(case 后面可以跟N个语句)
  10. [转]Go语言中的make和new
  11. ionic 网站放在服务器,Ionic4 服务器插件-服务器Httpd - Ionic Native
  12. 使用微软原生工具,反编译DLL并修改后再生成DLL
  13. python爬虫爬取微信公众号的阅读数、喜爱数、文章标题和链接等信息
  14. PHP将uncode转utf8,一行代码解决问题
  15. 真人玩计算机图片大全集,微信真人表情图片大全 用自己的照片做微信真人表情包(好玩),各类搞笑素材任你选择...
  16. matlab中opc没有注册类,电脑中出现没有注册类别的错误提示的多种解决方法
  17. larval框架的获取并存储(cache的使用)
  18. mac 字体微软雅黑字体_如何在Mac上验证和删除字体
  19. redhat安装配置Apache服务
  20. 5-4 九宫格输入法 (15分)

热门文章

  1. 单片机课程设计:基于STM32的温湿度检监测报警系统的设计
  2. 利用积分区域的对称性计算重积分
  3. pyecharts折线图上symbol(小圆圈)颜色的修改方法
  4. excel查找空值快捷键_CTRL+G,EXCEL中你必须要学会的快捷键
  5. PCI/CA体系下使用USBkey实现认证与加密(一)整体架构
  6. cf1341 Nastya and Door
  7. 中科院计算机研究生推免面试,【中科院信工所学生兼职面试】研究生推免面试经验分享。-看准网...
  8. data类型的Url格式--url(data:image/gif;base64,AAAA):把小数据直接嵌入到Url中
  9. 第七届 Sky Hackathon 笔记集合贴
  10. 双向长短期记忆网络模型_一种基于双向长短期记忆网络的定位修正方法与流程...