一些前置知识可以看一下我的联赛前数学知识

如何判断一个数是否为质数

方法一:试除法

扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是质数,否则\(n\)是合数。

代码

  1. bool is_prime(int n){
  2. if(n<2) return 0;
  3. int m=sqrt(n);
  4. for(int i=2;i<=m;i++){
  5. if(n%i==0) return 0;
  6. }
  7. return 1;
  8. }

方法二、线性筛

用 \(O(n)\) 的复杂度筛出所有的质数,然后 \(O(1)\) 判断

代码

  1. const int maxn=1e6+5;
  2. int pri[maxn];
  3. bool not_prime[maxn];
  4. void xxs(int n){
  5. not_prime[0]=not_prime[1]=1;
  6. for(int i=2;i<=n;++i){
  7. if(!not_prime[i]){
  8. pri[++pri[0]]=i;
  9. }
  10. for (int j=1;j<=pri[0] && i*pri[j]<=n;j++){
  11. not_prime[i*pri[j]]=1;
  12. if(i%pri[j]== 0) break;
  13. }
  14. }
  15. }

但是,我们来看一下这道题 LOJ#143. 质数判定

在 \(5s\) 的时间内判断 \(10^5\) 个 \(10^{18}\) 级别的数是否是质数

以上两种方法显然都不可行

所以我们要用到一种更高效的算法 \(Miller\ Rabin\)

Miller Rabin素数检测

根据费马小定理

若 \(p\) 为素数,对于任意整数 \(a\),都有

\(a^{p-1} \equiv 1(mod\ p)\)

它的逆定理在大多数情况下是成立的

所以,可以每一次随机挑选一些数 \(a\)

判断是否存在 \(a^{p-1} \equiv 1(mod\ p)\)

只要有一个 \(a\) 不满足条件,就将其标记为合数

否则标记为质数

代码

  1. #include<cstdio>
  2. #include<ctime>
  3. #include<cstdlib>
  4. #define rg register
  5. typedef long long ll;
  6. ll gsc(rg ll ds,rg ll zs,rg ll mod){
  7. return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+mod)%mod;
  8. }
  9. ll ksm(rg ll ds,rg ll zs,rg ll mod){
  10. rg ll nans=1;
  11. while(zs){
  12. if(zs&1LL) nans=gsc(nans,ds,mod);
  13. ds=gsc(ds,ds,mod);
  14. zs>>=1LL;
  15. }
  16. return nans;
  17. }
  18. int main(){
  19. rg ll aa,bb;
  20. rg bool jud=1;
  21. while(scanf("%lld",&aa)!=EOF){
  22. jud=1;
  23. if(aa==1){
  24. printf("N\n");
  25. continue;
  26. }
  27. for (rg int i=1;i<=100;i++) {
  28. bb=1LL*rand()*rand()%(aa-1)+1;
  29. if(ksm(bb,aa-1,aa)!=1){
  30. jud=0;
  31. break;
  32. }
  33. }
  34. if(jud) printf("Y\n");
  35. else printf("N\n");
  36. }
  37. return 0;
  38. }

但是也有例外,即存在一种极端反例卡迈克尔数(一种合数)

对于任何卡迈克尔数,费马定理都成立

虽然这种数很少,但是还是有可能会被卡(只有\(33\)分)

所以我们需要借助另一个定理来优化它

二次探测定理:若 \(p\) 为质数且 \(x∈(0,p)\),则方程 \(x^2≡1(mod\ p)\) 的解为 \(x_1=1,x_2=p-1\)

对于任意一个奇素数 \(p\) ,\(p-1\) 一定可以写成 \(r2^t\) 的形式

因此我们可以把费马小定理写成 \(a^{r2^t}\equiv 1(mod\ p)\) 的形式

一开始,我们先求出 \(a^r mod\ p\) 的值

然后每一次给这个值平方,一共平方 \(t\) 次

算一下每次得出来的结果是否满足二次探测定理

如果不满足,说明这个数不是质数

最后再看一下最终的值是否满足费马小定理即可

时间复杂度 \(klog^2n\)

其中 \(k\) 为每次检测的次数

事实证明,这个算法出错的概率非常小

为 \(\frac{1}{4^k}\)

代码

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<iostream>
  6. #define rg register
  7. inline int read(){
  8. rg int x=0,fh=1;
  9. rg char ch=getchar();
  10. while(ch<'0' || ch>'9'){
  11. if(ch=='-') fh=-1;
  12. ch=getchar();
  13. }
  14. while(ch>='0' && ch<='9'){
  15. x=(x<<1)+(x<<3)+(ch^48);
  16. ch=getchar();
  17. }
  18. return x*fh;
  19. }
  20. typedef long long ll;
  21. const int times=7;
  22. ll gsc(rg ll ds,rg ll zs,rg ll mod){
  23. return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+2*mod)%mod;
  24. }
  25. ll ksm(rg ll ds,rg ll zs,rg ll mod){
  26. rg ll nans=1;
  27. while(zs){
  28. if(zs&1LL) nans=gsc(nans,ds,mod);
  29. ds=gsc(ds,ds,mod);
  30. zs>>=1LL;
  31. }
  32. return nans;
  33. }
  34. bool check(rg ll a,rg ll r,rg ll t,rg ll mod){
  35. ll nans=ksm(a,r,mod),tmp=nans;
  36. for(rg ll i=1;i<=t;i++){
  37. nans=gsc(tmp,tmp,mod);
  38. if(nans==1 && tmp!=1 && tmp!=mod-1) return 0;
  39. tmp=nans;
  40. }
  41. if(tmp==1) return 1;
  42. else return 0;
  43. }
  44. bool Miller_Robin(rg ll n){
  45. if(n==2) return 1;
  46. if(n<2 || (n&1LL)==0) return 0;
  47. rg ll t=0,r=n-1;
  48. while((r&1LL)==0){
  49. r>>=1;
  50. t++;
  51. }
  52. for(rg int i=1;i<=times;i++){
  53. rg ll a=rand()%(n-1)+1;
  54. if(!check(a,r,t,n)) return 0;
  55. }
  56. return 1;
  57. }
  58. ll x;
  59. int main(){
  60. srand(time(0));
  61. while(scanf("%lld",&x)!=EOF){
  62. if(Miller_Robin(x)) printf("Y\n");
  63. else printf("N\n");
  64. }
  65. return 0;
  66. }

Pollard Rho算法

模板题

这道题不仅要判断是否是质数,还要求输出最大质因子

判质数的话用 \(Miller\ Rabin\) 判一下就好了

关键是怎么找出最大质因子

其实还是随机的思想

我们每一次随机一个数 \(n\),判断 \(n\) 是不是 \(p\) 的因数

如果是,就分成两个子问题 \(n\) 和 \(\frac{p}{n}\) 递归求解

如果当前的数为质数就更新答案

但是这样随机概率非常小

利用生日悖论,采用组合随机采样的方法,满足答案的组合比单个个体要多一些.这样可以提高正确率

具体方法是随机两个整数 \(n\),\(m\)

判断 \(|n-m|\) 是不是 \(p\) 的因数

看起来没有什么不同,实际上效率提高了不少

剩下的就在于怎么构造随机的 \(n,m\) 了

构造的方法会影响到我们程序的效率

实践证明,直接 \(rand\) 效率极低

而构造一种形如 \(f(x)=x^2+c\) 的数列效率很高

我们首先 \(rand\) 一个 \(x_1\)

然后令 \(f(x_2)=x_1^2+c\)

\(f(x_3)=x_2^2+c\)

依次类推,每次把序列相邻两项作差判断是否是 \(p\) 的因数

但是因为我们是在模意义下进行运算,所以这个序列一定有循环节

所以当遇到循环节的时候我们就退出循环,重新构造一个数列

因为每次求 \(gcd\) 的开销太大

所以我们可以先把相邻两项的差连乘起来,这是不影响结果的

当累乘的次数等于我们设定的一个值时再进行求 \(gcd\) 的运算

一般把这个值设为 \(2^k\)

代码

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<iostream>
  6. #include<cmath>
  7. #define rg register
  8. inline int read(){
  9. rg int x=0,fh=1;
  10. rg char ch=getchar();
  11. while(ch<'0' || ch>'9'){
  12. if(ch=='-') fh=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0' && ch<='9'){
  16. x=(x<<1)+(x<<3)+(ch^48);
  17. ch=getchar();
  18. }
  19. return x*fh;
  20. }
  21. typedef long long ll;
  22. const int times=10;
  23. int nt;
  24. ll x,ans;
  25. ll gsc(rg ll ds,rg ll zs,rg ll mod){
  26. return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+mod)%mod;
  27. }
  28. ll ksm(rg ll ds,rg ll zs,rg ll mod){
  29. rg ll nans=1;
  30. while(zs){
  31. if(zs&1LL) nans=gsc(nans,ds,mod);
  32. ds=gsc(ds,ds,mod);
  33. zs>>=1LL;
  34. }
  35. return nans;
  36. }
  37. bool check(rg ll a,rg ll r,rg ll t,rg ll mod){
  38. ll nans=ksm(a,r,mod),tmp=nans;
  39. for(rg ll i=1;i<=t;i++){
  40. nans=gsc(tmp,tmp,mod);
  41. if(nans==1 && tmp!=1 && tmp!=mod-1) return 0;
  42. tmp=nans;
  43. }
  44. if(tmp==1) return 1;
  45. else return 0;
  46. }
  47. bool Miller_Robin(rg ll n){
  48. if(n==2) return 1;
  49. if(n<2 || (n&1LL)==0) return 0;
  50. rg ll t=0,r=n-1;
  51. while((r&1LL)==0){
  52. r>>=1;
  53. t++;
  54. }
  55. for(rg int i=1;i<=times;i++){
  56. rg ll a=rand()%(n-1)+1;
  57. if(!check(a,r,t,n)) return 0;
  58. }
  59. return 1;
  60. }
  61. ll gcd(rg ll aa,rg ll bb){
  62. if(bb==0) return aa;
  63. return gcd(bb,aa%bb);
  64. }
  65. ll rp(rg ll x,rg ll y){
  66. rg int t=0,k=1;
  67. rg ll v0=rand()%(x-1)+1,v=v0,d,s=1;
  68. while(1){
  69. v=(gsc(v,v,x)+y)%x;
  70. s=gsc(s,std::abs(v-v0),x);
  71. if(v==v0 || !s) return x;
  72. if(++t==k){
  73. d=gcd(s,x);
  74. if(d!=1) return d;
  75. s=1,v0=v,k<<=1;
  76. }
  77. }
  78. }
  79. void dfs(rg ll n){
  80. if(n<=ans || n==1) return;
  81. if(Miller_Robin(n)){
  82. ans=n;
  83. return;
  84. }
  85. rg ll now=n;
  86. while(now==n) now=rp(n,rand()%n+1);
  87. while(n%now==0) n/=now;
  88. dfs(n),dfs(now);
  89. }
  90. ll solve(rg ll n){
  91. ans=1;
  92. dfs(n);
  93. return ans;
  94. }
  95. int main(){
  96. srand(time(0));
  97. scanf("%d",&nt);
  98. while(nt--){
  99. scanf("%lld",&x);
  100. if(Miller_Robin(x)) printf("Prime\n");
  101. else printf("%lld\n",solve(x));
  102. }
  103. return 0;
  104. }

Miller Rabin素数检测与Pollard Rho算法的更多相关文章

  1. Miller Rabin素数检测

    #include<iostream> #include<cstdio> #include<queue> #include<cstring> #inclu ...

  2. Pollard rho算法+Miller Rabin算法 BZOJ 3668 Rabin-Miller算法

    BZOJ 3667: Rabin-Miller算法 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1044  Solved: 322[Submit][ ...

  3. POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】

    Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...

  4. POJ2429_GCD &amp; LCM Inverse【Miller Rabin素数測试】【Pollar Rho整数分解】

    GCD & LCM Inverse Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9756Accepted: 1819 ...

  5. POJ1811_Prime Test【Miller Rabin素数測试】【Pollar Rho整数分解】

    Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...

  6. HDU1164_Eddy&#39;s research I【Miller Rabin素数测试】【Pollar Rho整数分解】

    Eddy's research I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  7. Pollard Rho算法浅谈

    Pollard Rho介绍 Pollard Rho算法是Pollard[1]在1975年[2]发明的一种将大整数因数分解的算法 其中Pollard来源于发明者Pollard的姓,Rho则来自内部伪随机 ...

  8. Pollard Rho 算法简介

    \(\text{update 2019.8.18}\) 由于本人将大部分精力花在了cnblogs上,而不是洛谷博客,评论区提出的一些问题直到今天才解决. 下面给出的Pollard Rho函数已给出散点 ...

  9. 初学Pollard Rho算法

    前言 \(Pollard\ Rho\)是一个著名的大数质因数分解算法,它的实现基于一个神奇的算法:\(MillerRabin\)素数测试(关于\(MillerRabin\),可以参考这篇博客:初学Mi ...

随机推荐

  1. burp-requests插件安装使用

    这段时间都没更博客,扫描器的更新也暂时停止了,因为回了学校之后需要准备实验室招新和几个比赛的事情,内疚两秒钟,赶快学习! burp里面的插件很多,但是不要被纷繁复杂的功能迷了双眼,还是那句话:适合自己 ...

  2. print(end="\r") 滚动输出到屏幕

    for i in range(10000): print(i, end = "\r") print(end="\r") 滚动输出到屏幕

  3. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

  4. Windows的API功能查询

    在逆向分析时,一些Windows的API函数往往是我们的突破口.但这些函数很难记得一清二楚,以下是我的查找办法,做个小结. 官网 https://docs.microsoft.com/en-us/wi ...

  5. CentOS配置Nginx官方的Yum源

    由于yum源中没有我们想要的nginx,那么我们就需要创建一个"/etc/yum.repos.d/nginx.repo"的文件,其实就是新增一个yum源. [root@niaoyu ...

  6. eclipse/myeclipse 使用技巧

    一.变量名自动补全 原理是:在输入变量名后,去掉按下空格或=后,代码上屏 以前只知道alt+/调出assist,后来发现可以所有字母都激活content assist(8.1里有写).用起来果然很爽, ...

  7. uni-app快速入门教程

    1.什么是uni-app? uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.H5.以及各种小程序(微信/支付宝/百度/头条/QQ/ ...

  8. 前端js实现九宫格模式抽奖(多宫格抽奖)

    介绍: 前端九宫格是一种常见的抽奖方式,js实现如下,掌握其原理,不论多少宫格,都可以轻松应对.(代码可复制直接运行看效果). 该案例以四宫格入门,可扩展多宫格,奖品模块的布局可自由设置. <! ...

  9. ES6新增api随记

    求一个数组中的最大值 Math.max.apply(null,[1,2,3,4])=>Math.max(...[1,2,3]) 讲一个数组中的元素全部添加到另一个数组中 let arr=[1,2 ...

  10. SpringBoot集成Swagger2并配置多个包路径扫描

    1. 简介   随着现在主流的前后端分离模式开发越来越成熟,接口文档的编写和规范是一件非常重要的事.简单的项目来说,对应的controller在一个包路径下,因此在Swagger配置参数时只需要配置一 ...