Miller_Rabbin&&Pollard_Rho 学习笔记

Intro

首先我们考虑这样一个问题

给定一个正整数 \(p \ (p<=1e8)\),请判断它是不是质数

妈妈我会试除法!

于是,我们枚举 \(\sqrt p\) 以内的所有数,就可以非常轻松地得到正确答案。

贴一小段代码

bool check(int n)
{
if(n==1) return false;
for(int i=2;i*i<=n;++i)
if(!(n%i)) return false;
return true;
}

\(Extra\):给定 \(p\ (p<=1e5)\) 个正整数 \(p\ (p<=1e8)\) ,请判断它是不是质数

妈妈我会埃氏筛!

所谓筛法,就是利用了一条最最基本的原理:素数的倍数一定是合数,于是,我们便可以在一定数据范围之内来解决多次询问一个数是否为素数的问题

埃氏筛便是基于这个最简单的思想

时间复杂度约为\(O(nlog_{2}n)\)

再贴一小段代码

void prime()
{
memset(isprime,0,sizeof isprime);
for(int i=2;i<=n;i++)
if(isprime[i]==0)
for(int j=i+i;j<=n;j+=i)
isprime[j]=1;
}

\(Extra^{2}\):给定 \(p\ (p<=1e7)\) 个正整数 \(p \ (p<=1e8)\),请判断它们是不是质数

妈妈我有信仰,我能写出常数极为优秀的埃氏筛

这显然是不现实的,那可以怎样解决这个问题呢?

爸爸我会欧拉筛!

我们会发现,在用埃氏筛解决问题的时候,我们仍然进行了一些不必要的操作,比如\(30\),在程序运行中我们用了\(2,3,5\)三个素数来证明其为合数,这显然是十分不合常理的。

所以我们考虑对其进行优化。

我们考虑,对于每一对\((i,prime[j])\), 显然$i*prime[j] $为合数,我们将其筛去

当 $ i \ mod \ prime[j] =0 $ 时就说明剩余的情况,我们会在以后考虑到,因为每一个合数都可以分解为一个合数(或一个质数)和一个质数的乘积,这样就可以避免重复考虑

时间复杂度约为 \(O(n)\)

再贴一小段代码

void prime()
{
memset(isprime,0,sizeof isprime);
for(int i=2;i<=n;i++)
{
if(isprime[i]==0)
++cnt,prime[t]=i;
for(int j=1;j<=t&&i*prime[j]<=n;++j)
{
isprime[i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
}

\(Extra^{3}\):给定 \(p\ (p<=1e4)\) 个正整数 \(p\ (p<=1e16)\),请判断它们是不是质数

好像没辙了

Miller-Rabbin 算法

所谓 \(\texttt{Miller-Rabbin}\) 素性测试算法,就是在极短的的时间内,通过一种玄学科学的判断方法,使得判断出错的概率尽可能小。也就是说,这是一个正确率可控,但不稳定的算法

但为什么要学它呢?

至少,在OI界内够用嘛

因为,我们可以通过人为的操作,将其错误率降到我们可以接受的范围内。


前置芝士:欧拉定理

即当 \(p\) 是质数时

\[a^{\varphi (p)}\equiv 1 (mod \ p)
\]

由 \(\varphi (p)=p-1\) 得

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

于是,既然当 \(p\) 是质数时,\(a^{p-1}\equiv 1 (mod \ p)\) 成立

那么,当 \(a^{p-1}\equiv 1 (mod \ p)\) 成立时,\(p\) 是否为质数呢?

显然不是!!!

那么,我们是否能够可以尝试一种方法,将一个数进行多次测试,这就是这种测试方法的一个初级想法。

但是,是否存在一些合数,能够通过所有的测试呢?

答案是肯定的。这样的书被人们称为\(\texttt{Carmichael}\)数,如 \(561,1105,1729\) ,有兴趣可以点击此链接

所以说,我们有没有一种办法,能够使得正确率进一步提高呢?

答案依然是肯定的。


二次探测

如果\(p\)为质数,且

\[x^2\equiv 1\pmod{p}
\]

那么 $ x=1 \ or \ p-1 $。

这个应该是显然吧,移项直接就可以得到。

然后,我们就可以利用这一个性质来完成 \(\texttt{Miller-Rabbin}\) 素性测试。


具体做法:

  • 将 \(p-1\) 提取出所有 \(2\) 的因数,假设有 \(num\) 个。设剩下的部分为 \(tot\)。

  • 枚举(或随机)一个底数 \(a\) 并计算 \(r=a^{tot}\pmod p\)。

  • 将 \(r\) 连续平方 \(num\) 次。如果没有出现过 \(p-1\) ,那么 \(p\) 未通过二次探测,不是质数。

  • 否则,若已经测试完毕,则跳出。否则回到第二步。

个人常用的模数:\(3,5,7,11,13,37,79,97\) (还是挺好记的吧)

进行一次二次探测的时间复杂度为为 \(O(\log_2n)\)。

所以测试一个数是否为素数的时间最坏为 \(O(T\log_2n)\),其中 \(T\) 为测试次数。

贴代码

typedef long long ll;
bool miller_rabbin(ll x,ll a,ll d)
{
if(x==1||(!(x&1))) return 0;
if(x==2) return 1;
while(!(d&1))
d>>=1;
ll t=ksm(a,d,x);
while(d!=x-1&&t!=1&&t!=x-1)
{
t=ksc(t,t,x);
d<<=1;
}
return t==x-1||(d&1)==1;
}
bool solve(ll n)
{
for(int i=1;i<=6;++i)
{
if(n==te[i]) return 1;
if(!miller(n,te[i],n-1)) return 0;
}
return 1;
}

Part II


Pollard-Rho


A Brief introduction

\(Pollard-Rho\)是一个基于随机算法的大整数分解算法,可以在较短时间内求出其因数。


考虑这样一个问题

从\(\left [2,n \right ]\)中随机一个数字,求其为n的约数的概率。

这个概率显然是很低的,不符合我们的期望

我们考虑优化这个算法

Birthday Trick (生日悖论)

我们稍微修改一下这个问题:

从[1,1000]中随机选取两个数  x 和 y,x−y=42 (x≠y) 的概率是多少?

能够粗略地算出,此时的概率大约为\(\frac{1}{500}\)。

如果我们不再坚持仅选取1个数并且这个数必须为42,而是能够选取2个数并且他们的差刚好等于42,那么,成功的概率被提高了。

如果我们在[1,1000]中选取 k 个数 x1,……,xk, 

取得的k个数中满足xi−xj=42的概率是多少呢?

大约在 k=30 的时候,成功的概率已经超过一半。 

我们有一个大区间[1,1000],但是仅仅生成约 30 个随机数就让我们成功的概率超过了一半,大约在 k=100 的时候,我们几乎可以确保我们是成功的。这是一个非常重要的观察, 它被称为生日悖论

以上摘自某国外论文


于是,我们就可以通过这样一个方法来大幅度的提高程序效率

为了使复杂度较稳定,我们可以使随机数不那么随机

根据\(f(x)=x^2+c\) 来随机生成,复杂度较优

但是这样生成可能有环出现,于是有两种方法可以来解决这个问题

i.floyd法

我们可以设置两个变量,像追击问题那样,让一个变量以另一个变量的二倍速来扩展,这样,当他们再一次相等时,至少跑过了一圈。

ii.倍增优化法

暂时略

然后关于本代码,由于为了通过Luogu上的毒瘤测试点,加了非常多的玄学优化,在此暂时不做叙述。

留坑,待填

贴代码

ll f(ll n,ll c,ll md)
{
return (ksc(n,n,md)+c)%md;
}
ll ans=0;
ll pollard_rho(ll n)
{
ll w=rand()%(n-1)+1;
ll a,b;
a=b=rand()%(n-1)+1;
ll i=0,j=1;
ll p=1;
ll z=1;
while(++i)
{
a=f(a,w,n);
if(b>a)p=b-a;
else p=a-b;
z=ksc(z,p,n);
if(a==b||(!z)) return n;
if(!(i%667)||i==j)
{
p=gcd(z,n);
if(p>1) return p;
if(i==j)
b=a,j<<=1;
}
}
return n;
}
void solve(ll n)//求一个数的最大质因数
{
if(n==1) return ;
if(n<ans) return ;
if(miller_rabbin(n))
{
ans=max(ans,n);
return;
}
ll t=n;
while(t==n)
t=pollard_rho(t);
solve(t);
solve(n/t);
}

Miller_Rabbin&&Pollard_Rho 学习笔记的更多相关文章

  1. 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)

    注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...

  2. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  3. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  4. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  5. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  6. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  7. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  8. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  9. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

随机推荐

  1. 如何在ADO中使用数据读取器(DataReader)读取数据

    DbDataReader类型(实现IDataReader接口)是从数据源获取信息最简单也最快速的方法. 数据读取器是只读向前的效据流.井且一次返回一条记录.因此.只有当你向数据源提交 Select 查 ...

  2. SpringCloud-sleuth-zipkin链路追踪

    什么是Zipkin? 它是一个分布式链路跟踪系统它可以帮助收集时间数据,每个应用程序向Zipkin报告定时数据,Zipkin UI呈现了一个依赖图表来展示多少跟踪请求经过了每个应用程序:如果想解决延迟 ...

  3. Java高阶语法---static

    背景:听说static Java高阶语法是挺进BAT必经之路. static: 静态static,很多时候会令我望文生义,但是get到了static最重要的一点,其他的理解都还ok. static最重 ...

  4. PHP信号管理

    PHP信号管理   SIGHUP     终止进程     终端线路挂断 SIGINT     终止进程     中断进程 SIGQUIT    建立CORE文件终止进程,并且生成core文件 SIG ...

  5. HTTP概念解析

    HTTP--Hyper Text Transfer Protocol HTTP详细介绍(火星的小白 51CTO): https://blog.51cto.com/13570193/2108347 先进 ...

  6. python迭代器与生成器及yield

    一.迭代器(itertor) 1.可迭代: 在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable). 其中__iter__( ...

  7. Delphi IfThen语句

    function IfThen(AValue: Boolean; const ATrue: string; AFalse: string = ''): string; overload; $[StrU ...

  8. vue watch监听验证码时,axios延迟发送post请求。

    标题写的全面一些,方便其他人检索,我就是找了半天找不到资料,最后自己搞定了. 原理: 每次监听到输入值变化,就打一个时间戳,然后暂停2秒再去提交post验证. 但是每次提交前,判断一下之前打的时间戳和 ...

  9. java加载properties文件的六中基本方式实现

    java加载properties文件的方式主要分为两大类:一种是通过import java.util.Properties类中的load(InputStream in)方法加载: 另一种是通过impo ...

  10. shell脚本-正则、grep、sed、awk

    ----------------------------------------正则---------------------------------------- 基础正则 ^word ##搜索以w ...