Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识
如何判断一个数是否为质数
方法一:试除法
扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是质数,否则\(n\)是合数。
代码
bool is_prime(int n){
if(n<2) return 0;
int m=sqrt(n);
for(int i=2;i<=m;i++){
if(n%i==0) return 0;
}
return 1;
}
方法二、线性筛
用 \(O(n)\) 的复杂度筛出所有的质数,然后 \(O(1)\) 判断
代码
const int maxn=1e6+5;
int pri[maxn];
bool not_prime[maxn];
void xxs(int n){
not_prime[0]=not_prime[1]=1;
for(int i=2;i<=n;++i){
if(!not_prime[i]){
pri[++pri[0]]=i;
}
for (int j=1;j<=pri[0] && i*pri[j]<=n;j++){
not_prime[i*pri[j]]=1;
if(i%pri[j]== 0) break;
}
}
}
但是,我们来看一下这道题 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\) 不满足条件,就将其标记为合数
否则标记为质数
代码
#include<cstdio>
#include<ctime>
#include<cstdlib>
#define rg register
typedef long long ll;
ll gsc(rg ll ds,rg ll zs,rg ll mod){
return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+mod)%mod;
}
ll ksm(rg ll ds,rg ll zs,rg ll mod){
rg ll nans=1;
while(zs){
if(zs&1LL) nans=gsc(nans,ds,mod);
ds=gsc(ds,ds,mod);
zs>>=1LL;
}
return nans;
}
int main(){
rg ll aa,bb;
rg bool jud=1;
while(scanf("%lld",&aa)!=EOF){
jud=1;
if(aa==1){
printf("N\n");
continue;
}
for (rg int i=1;i<=100;i++) {
bb=1LL*rand()*rand()%(aa-1)+1;
if(ksm(bb,aa-1,aa)!=1){
jud=0;
break;
}
}
if(jud) printf("Y\n");
else printf("N\n");
}
return 0;
}
但是也有例外,即存在一种极端反例卡迈克尔数(一种合数)
对于任何卡迈克尔数,费马定理都成立
虽然这种数很少,但是还是有可能会被卡(只有\(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}\)
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iostream>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
typedef long long ll;
const int times=7;
ll gsc(rg ll ds,rg ll zs,rg ll mod){
return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+2*mod)%mod;
}
ll ksm(rg ll ds,rg ll zs,rg ll mod){
rg ll nans=1;
while(zs){
if(zs&1LL) nans=gsc(nans,ds,mod);
ds=gsc(ds,ds,mod);
zs>>=1LL;
}
return nans;
}
bool check(rg ll a,rg ll r,rg ll t,rg ll mod){
ll nans=ksm(a,r,mod),tmp=nans;
for(rg ll i=1;i<=t;i++){
nans=gsc(tmp,tmp,mod);
if(nans==1 && tmp!=1 && tmp!=mod-1) return 0;
tmp=nans;
}
if(tmp==1) return 1;
else return 0;
}
bool Miller_Robin(rg ll n){
if(n==2) return 1;
if(n<2 || (n&1LL)==0) return 0;
rg ll t=0,r=n-1;
while((r&1LL)==0){
r>>=1;
t++;
}
for(rg int i=1;i<=times;i++){
rg ll a=rand()%(n-1)+1;
if(!check(a,r,t,n)) return 0;
}
return 1;
}
ll x;
int main(){
srand(time(0));
while(scanf("%lld",&x)!=EOF){
if(Miller_Robin(x)) printf("Y\n");
else printf("N\n");
}
return 0;
}
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\)
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
typedef long long ll;
const int times=10;
int nt;
ll x,ans;
ll gsc(rg ll ds,rg ll zs,rg ll mod){
return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+mod)%mod;
}
ll ksm(rg ll ds,rg ll zs,rg ll mod){
rg ll nans=1;
while(zs){
if(zs&1LL) nans=gsc(nans,ds,mod);
ds=gsc(ds,ds,mod);
zs>>=1LL;
}
return nans;
}
bool check(rg ll a,rg ll r,rg ll t,rg ll mod){
ll nans=ksm(a,r,mod),tmp=nans;
for(rg ll i=1;i<=t;i++){
nans=gsc(tmp,tmp,mod);
if(nans==1 && tmp!=1 && tmp!=mod-1) return 0;
tmp=nans;
}
if(tmp==1) return 1;
else return 0;
}
bool Miller_Robin(rg ll n){
if(n==2) return 1;
if(n<2 || (n&1LL)==0) return 0;
rg ll t=0,r=n-1;
while((r&1LL)==0){
r>>=1;
t++;
}
for(rg int i=1;i<=times;i++){
rg ll a=rand()%(n-1)+1;
if(!check(a,r,t,n)) return 0;
}
return 1;
}
ll gcd(rg ll aa,rg ll bb){
if(bb==0) return aa;
return gcd(bb,aa%bb);
}
ll rp(rg ll x,rg ll y){
rg int t=0,k=1;
rg ll v0=rand()%(x-1)+1,v=v0,d,s=1;
while(1){
v=(gsc(v,v,x)+y)%x;
s=gsc(s,std::abs(v-v0),x);
if(v==v0 || !s) return x;
if(++t==k){
d=gcd(s,x);
if(d!=1) return d;
s=1,v0=v,k<<=1;
}
}
}
void dfs(rg ll n){
if(n<=ans || n==1) return;
if(Miller_Robin(n)){
ans=n;
return;
}
rg ll now=n;
while(now==n) now=rp(n,rand()%n+1);
while(n%now==0) n/=now;
dfs(n),dfs(now);
}
ll solve(rg ll n){
ans=1;
dfs(n);
return ans;
}
int main(){
srand(time(0));
scanf("%d",&nt);
while(nt--){
scanf("%lld",&x);
if(Miller_Robin(x)) printf("Prime\n");
else printf("%lld\n",solve(x));
}
return 0;
}
Miller Rabin素数检测与Pollard Rho算法的更多相关文章
- Miller Rabin素数检测
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #inclu ...
- Pollard rho算法+Miller Rabin算法 BZOJ 3668 Rabin-Miller算法
BZOJ 3667: Rabin-Miller算法 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1044 Solved: 322[Submit][ ...
- POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】
Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...
- POJ2429_GCD & LCM Inverse【Miller Rabin素数測试】【Pollar Rho整数分解】
GCD & LCM Inverse Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9756Accepted: 1819 ...
- POJ1811_Prime Test【Miller Rabin素数測试】【Pollar Rho整数分解】
Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...
- HDU1164_Eddy'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 ...
- Pollard Rho算法浅谈
Pollard Rho介绍 Pollard Rho算法是Pollard[1]在1975年[2]发明的一种将大整数因数分解的算法 其中Pollard来源于发明者Pollard的姓,Rho则来自内部伪随机 ...
- Pollard Rho 算法简介
\(\text{update 2019.8.18}\) 由于本人将大部分精力花在了cnblogs上,而不是洛谷博客,评论区提出的一些问题直到今天才解决. 下面给出的Pollard Rho函数已给出散点 ...
- 初学Pollard Rho算法
前言 \(Pollard\ Rho\)是一个著名的大数质因数分解算法,它的实现基于一个神奇的算法:\(MillerRabin\)素数测试(关于\(MillerRabin\),可以参考这篇博客:初学Mi ...
随机推荐
- 转:Chrome浏览器查看网站登录 Cookie 信息的方法
当我们使用自动签到等程序的时候一般都要用到网站Cookie,我们可以借助浏览器的扩展来获取Cookie信息,但其实通过浏览器本身的功能就可以查看Cookie信息.以Chrome类浏览器为例有以下三种方 ...
- Object.prototype.toString.call()为什么可以用来检测数据类型?
obj.toString()方法是用来干什么的 每一个对象都有一个toString()方法,默认情况下toString()被每一个Object对象继承,如果此方法未被重写,toString()返回&q ...
- Fiddle重定向请求
以当当网和淘宝网为例: 1.打开浏览器,在地址栏中输入www.dangdang.com,进入当当主页. 2.在规则编辑器中设置规则,将dangdang重定向至taobao,并打开规则. 3.再次刷新当 ...
- 安装nginx并安全地配置和启动
一.安装nginx >>参考文章<< 安装教程,看代码&注释 # .sh # 如果centos服务器是最低安装,则先安装weget yum install -y wge ...
- Srcum冲刺_Day05
一.团队展示: 1.项目:light_note备忘录 2.队名:删库跑路队 3.团队成员 队员(不分先后) 项目角色 黄敦鸿 后端工程师.测试 黄华 后端工程师.测试 黄骏鹏 后端工程师.测试 黄源钦 ...
- ip 子网掩码、网络地址、广播地址计算
例:已知ip 16.158.165.91/22子网掩码 根据22 得知子网掩码占22位 即:11111111.11111111.11111100.00000000 == 255.255.252. ...
- 深入解析volatile关键字
前言 很高兴遇见你~ 欢迎阅读我的文章. volatile关键字在Java多线程编程编程中起的作用是很大的,合理使用可以减少很多的线程安全问题.但其实可以发现使用这个关键字的开发者其实很少,包括我自己 ...
- 题解 CF1062E Company
\(\texttt{Solution}\) 数据结构学傻的蒟蒻来写一个新思路 这题的正解是利用多个结点的 \(lca\) 是 \(dfs\) 序最大的结点和 \(dfs\) 序最小的结点的 \(lca ...
- 面试官:小伙子,你给我简单说一下RocketMQ 整合 Spring Boot吧
前言 在使用SpringBoot的starter集成包时,要特别注意版本.因为SpringBoot集成RocketMQ的starter依赖是由Spring社区提供的,目前正在快速迭代的过程当中,不同版 ...
- STL——容器(Map & multimap)的简述与构造
1. map/multimap 的简介 map 是标准的关联式容器,一个 map 里存储的元素是一个键值对序列,叫做 (key,value) 键值对.它提供基于 key 快速检索数据的能力. map ...