文章目录

  • 前置芝士
    • 快速乘
      • O(1)
      • O(log)
    • floyd提出的判环法
      • 判环
      • 找环
    • 生日悖论
  • Miller_rabin
    • 前言
    • 二次探测
      • 代码
  • Pollard-Rho
    • 前言
    • 例题
    • 构建随机数列
    • 优(ka)化(chang)
    • 注意事项
    • 时间复杂度证明
    • 代码

因为原来的那篇已经很多了,所以在此写上第二篇。

这一章可以说是紧紧围绕的素数的主旨展开的。

前置芝士

快速乘

博主博主,平常 O ( 1 ) O(1) O(1)都已经如此之快,难道可以 O ( 0 ) O(0) O(0)?

不不不,都一样,只不过算的是 x ∗ y % z x*y\%z x∗y%z,因为有时候 x ∗ y x*y x∗y溢出了long long,但是结果并没有,所以发明了快速乘。

O(1)

O ( 1 ) O(1) O(1)版的非常简单。 x ∗ y % z = x ∗ y − ( x ∗ y / z ) ∗ z x*y\%z=x*y-(x*y/z)*z x∗y%z=x∗y−(x∗y/z)∗z(在C++环境下),但是这个有什么特殊的吗?

就是用溢出对待溢出,我们先用long double(16位)得出(x*y/z)(你在比赛的时候,可以先用 s i z e o f ( l o n g sizeof(long sizeof(long d o u b l e ) double) double)得出你电脑的long double是几位的,如果不是 16 16 16位的话,那评测机应该也是,为了稳妥还是用 l o g log log的吧)。

然后我们乘一下,两边可能会溢出,但是我们还是能减出正确的结果。

另外还有一坨精度问题,模数大的话还是少用吧。

inline  LL  ksc(LL  x,LL  y,LL  z)
{LL  c=(LD)x*y/z+0.5;LL  ans=x*y-c*z;return  ans<0?ans+z:ans;
}

O(log)

有没有什么稳得一批又好用的快速乘?

当然后,假设又有个 x , y , z x,y,z x,y,z。

我们把 x x x拆成二进制: c n ∗ 2 n + c n − 1 ∗ 2 n − 1 + . . . . c_{n}*2^{n}+c_{n-1}*2^{n-1}+.... cn​∗2n+cn−1​∗2n−1+....,而 c c c的取值只能为 0 , 1 0,1 0,1
然后 x ∗ y x*y x∗y在乘法分配率一下: y ∗ c 0 ∗ 2 0 + y ∗ c 1 ∗ 2 1 + . . . y*c_{0}*2^{0}+y*c_{1}*2^{1}+... y∗c0​∗20+y∗c1​∗21+...,那么我们只要边乘边模不就好起来了吗。

inline ll ksc(ll x,ll y,ll p){//计算x乘y的积ll res=0;//加法初始化while(y){if(y&1)res=(res+x)%p;//模仿二进制x=(x<<1)%p; y>>=1;//将x不断乘2达到二进制}return res;
}
// ll 表示 long long
//这里的代码用的是https://www.cnblogs.com/812-xiao-wen/p/10543023.html

floyd提出的判环法

判环

你以为是floyd,不是,是这样的,假设一个链表有环,怎么判环,我们这样想: y y y以 x x x两倍的速度奔跑,那么当 x , y x,y x,y相遇时, y y y刚好跑完几圈了,就退出。

找环

其实还有个扩展,如何找到环的起始位置。

我们设链表头走到环开始的地方步数为 m m m,从链表头走到相遇地点的步数是 m + k m+k m+k,然后环的长度为 n n n, x , y x,y x,y分别走了 X , Y X,Y X,Y圈。

那么 S x = m + k + X n , S y = m + k + Y n S_{x}=m+k+Xn,S_{y}=m+k+Yn Sx​=m+k+Xn,Sy​=m+k+Yn,然后又因为 S y = 2 S x S_{y}=2S_{x} Sy​=2Sx​,所以 S x = ( Y − X ) n S_{x}=(Y-X)n Sx​=(Y−X)n。
也就是说两人走的距离肯定是 n n n的倍数。

然后我们再把 x x x提到了链表开始的地方,让两个人继续开始走,当走了 m m m步( x x x在环开始的地方),那么因为说了 S y = 2 S x = 2 ( Y − X ) n S_{y}=2S_{x}=2(Y-X)n Sy​=2Sx​=2(Y−X)n,也就是说 y y y走的步数应该是 n n n的倍数,也就是说当第一次相遇的时候,他应该离走完这个环到环开始的地方为 m m m,所以 y y y也到了环开始的地方,所以 x , y x,y x,y将会在环开始的地方相遇。

可惜这里不用。

生日悖论

首先我们来看看,现在我们来选数字,在1-100之间,如果我们选一个数字,那么是1的概率则是 1 100 \frac{1}{100} 1001​,但是如果我们选两个数字,然后取差的绝对值,会怎样?我们选一个数字,然后选到他周围的两个数字的概率就变成了 1 50 \frac{1}{50} 501​了!(忽略第一个数字 1 1 1和 100 100 100的情况,那还是 1 100 \frac{1}{100} 1001​),难道多元能增加概率!

没错,这就是生日悖论的内容。

生日悖论的重要思想是什么, 1 − x 1-x 1−x的范围,如果有 x \sqrt{x} x ​个数字的话,重复的概率就会高达 50 % 50\% 50%,恐不恐怖。

至于证明,在这里贴上大佬的证明。

Miller_rabin

相信在第一章里面,你们已经学会了费马小定律了,那就不讲了QMQ。

前言

我们都知道判断一个数字是不是素数,有一种方法就是试除法,直接从 2 2 2枚举到 p \sqrt{p} p ​,但是有没有一种方法,能比 O ( p ) O(\sqrt{p}) O(p ​)还快有准确无误呢?

答案是并没有,但是如果你要求的是很大概率的话,打我可以告诉你的是,Miller_rabin就是这么一种算法,基本上准确无误,就连强伪素数都能跑过去,是什么呢?

二次探测

我们都知道,选取一个 p p p数字,然后用费马小定理判断一下,如果不是 1 1 1,那就不是素数,但是存在这么一种数字,能满足费马小定理但是不是素数的一类数字,我们又要怎么判断呢?

这里就要引入一个定理了,这个定理可以很大概率的判断是不是素数,加上费马小定理。

如果 p p p是质数且 a 2 ≡ 1 ( m o d p ) ( a < p ) a^2≡1(\mod p)(a<p) a2≡1(modp)(a<p),那么 a = 1 , p − 1 a=1,p-1 a=1,p−1。
我们可以来证明一下:
a 2 ≡ 1 ( m o d p ) a^2≡1(\mod p) a2≡1(modp)
a 2 − 1 ≡ ( m o d p ) a^2-1≡(\mod p) a2−1≡(modp)
( a + 1 ) ( a − 1 ) ≡ 0 ( m o d p ) (a+1)(a-1)≡0(\mod p) (a+1)(a−1)≡0(modp)
那么,因为 a < p a<p a<p且 p p p是质数,所以 a = 1 , p − 1 a=1,p-1 a=1,p−1。

那么我们就可以把 p − 1 p-1 p−1分成 2 t ∗ k 2^{t}*k 2t∗k,然后随机选取一个值 x x x,然后计算 x k x^{k} xk,然后继续不断取平方: x 2 i ∗ k x^{2^{i}*k} x2i∗k,然后不断的用二次探测来检测,更重要的是我们最后还可以用用费马小定理,当然, x x x我们可以手动取几个素数来多判几次,不知道为什么,素数成功概率大一点QMQ。

很明显是 l o g log log的。

代码

inline  LL  ksc(LL  x,LL  y,LL  z)
{LL  c=(LD)x*y/z+0.5;LL  ans=x*y-c*z;return  ans<0?ans+z:ans;
}
inline  LL  ksm(LL  x,LL  m,LL  mod)
{if(m==0)return  1%mod;LL  ans=1;while(m>1){m&1?ans=ksc(ans,x,mod):0;x=ksc(x,x,mod);m>>=1;}return  ksc(ans,x,mod);
}
inline  int  log2(LL  &x)
{int  ans=0;while(x%2==0)ans++,x>>=1;return  ans;
}
int  su[]={2,3,5,7,11,23,29,61};
inline  bool  pd(LL  x)//判断一个素数
{for(int  i=0;i<=7;i++){if(x<=su[i])return  1;LL  y=x-1;int  tt=log2(y);y=ksm(su[i],y,x);while(tt--){LL  z=ksc(y,y,x);if(z==1  &&  y!=1  &&  y!=x-1)return  0;y=z;}if(y!=1)return  0;}return  1;
}

Pollard-Rho

前言

你是否想快速的分解一个素数?

想吗?少年。

—来自SaDiao博主的一席话。

例题

都看到了,就是想咯QMQ。

例题

假设我们要分的数字是 p p p

构建随机数列

我们构建一个随机函数 x i = x i − 1 ∗ x i − 1 + C ( m o d p ) x_{i}=x_{i-1}*x_{i-1}+C(\mod p) xi​=xi−1​∗xi−1​+C(modp)( C C C为我们自己定的常数),这么强?

同时 x 1 = 2 x_{1}=2 x1​=2,我们发现这个序列每个都跟前面的数字有关,那么不就是类似链表了吗?而且因为是模了后序列,所以会有环(根据生日悖论,出现相同的数字概率是 O ( p ) O(\sqrt{p}) O(p ​)),就可以用 f l o y d floyd floyd判环法了。

我们再设一个函数: y i = x i − 1 ∗ x i − 1 + C ( m o d q ) y_{i}=x_{i-1}*x_{i-1}+C(\mod q) yi​=xi−1​∗xi−1​+C(modq)( q q q为 p p p的一个质因数), y 1 = 2 y_{1}=2 y1​=2,那么这个序列也会出现循环节的,我们再在两个循环节上找到两个位置 i , j i,j i,j,使得 i < j , l ( i ) = l ( j ) i<j,l(i)=l(j) i<j,l(i)=l(j),然后我们就会发现 ∣ x i − x j ∣ |x_{i}-x_{j}| ∣xi​−xj​∣含有 q q q,也就是 g c d ( ∣ x i − x j ∣ , p ) ≠ 1 gcd(|x_{i}-x_{j}|,p)≠1 gcd(∣xi​−xj​∣,p)​=1,那么我们就可以找到一个素数了。

但是我们并不知道 q q q,我们又怎么找到 i , j i,j i,j呢,我们会发现其实 i , j i,j i,j就是 y y y数列循环节上的对应位置,而上面也只是提供了一种可行性,也就是说我们可以用 f l o y d floyd floyd找环法来在 x x x数列中找,如果 g c d gcd gcd为 1 1 1,那么继续找环,如果 g c d gcd gcd为 p p p(差为0就会这样),说明我们找到了模数为 q q q的环,可惜也是模数为 p p p的环,那么我们就退出,然后 C + + C++ C++,如果两个都不是,那么我们不就找到了一个质因数了吗?

再看看概率有多大,原本我们找两个数字的差找到质因数的概率应该比较小,但是如果我们是 g c d ≠ 1 gcd≠1 gcd​=1的话,那概率不就大了吗?而且期望的循环节大小为 p \sqrt{p} p ​,不就好起来了吗?

而且判断一个数字是不是素数就靠Miller_rabin了。

优(ka)化(chang)

我们要发现一个事情: x % z x\%z x%z,可以等同于 z ∗ ( ( x / g c d ( x , z ) ) % ( z / g c d ( x , z ) ) ) z*((x/gcd(x,z))\%(z/gcd(x,z))) z∗((x/gcd(x,z))%(z/gcd(x,z)))。

那么这有什么用呢?我们可以把几个差乘起来,然后模一下(如果出现了质因子的话不会因为模了而消失掉,上面写了),至于乘几次,我选择的是 127 127 127,当然, p o w ( p , 0.1 ) pow(p,0.1) pow(p,0.1)也有人用,不要太大就可以了,我们后面则叫乘了 s t e p step step次。

那么我们也是乘完后GCD。

  1. 如果为 1 1 1继续。
  2. 如果为 n n n的话,说不定中间有质因子呢,我们也回到 s t e p step step次一起,不乘起来,一次次慢慢来。
  3. 如果两个都不是,恭喜,喜提质因子一枚。

然后自我感觉良好,优化了不少的常数。

注意事项

我们会发现,打完代码有事后还是会卡住的。

为什么,因为 4 4 4是个神奇的数字,我们可以把 C C C从 1 1 1到 4 4 4枚举一遍,会发现差统统不会涉及到 2 2 2,而其他 2 2 2的次方,比如 2 i 2^{i} 2i,在 C = 2 i − 4 C=2^{i}-4 C=2i−4的时候,肯定跳一次就能得到结果, x = 0 , y = C x=0,y=C x=0,y=C,然后就可以把 2 2 2筛出来,为什么 4 4 4不行,因为 C = 0 C=0 C=0了,所以我们就只会一遍遍的得到 4 4 4然后重来。

那我们特判 4 4 4不就行了?不不不,特判要讲究艺术。

你想想,我们要是特判 % 2 = = 0 \%2==0 %2==0不是一样的吗,而且还造福了其他的数字,尤其是 2 2 2的次方,不加这个很有可能就老是到 2 i − 4 2^{i}-4 2i−4才跳出来。

时间复杂度证明

一次的Pollard-Rho的复杂度是多少?(这道题目得用几次Pollard-Rho)

设 N = A ∗ B ( A < = B ) N=A*B(A<=B) N=A∗B(A<=B),那么 A < = ( N ) A<=\sqrt(N) A<=( ​N),我们是在 x x x找 A A A的循环节,期望复杂度为 O ( A ) O(\sqrt{A}) O(A ​),那么不就是 O ( N 1 4 ) O(N^{\frac{1}{4}}) O(N41​)吗?

但是其实不然,因为加上GCD什么乱起八糟的,正宗的应该是:
O ( N 1 4 l o g N s t e p + l o g N ) O(\frac{N^{\frac{1}{4}}logN}{step}+logN) O(stepN41​logN​+logN),但原本就是玄学算法你加这么多干嘛,而且在long long范围内我们的 s t e p step step肯定大于 l o g N logN logN,毕竟我们的 s t e p step step原本就是 127 127 127吗。

所以我们还是写 O ( N 1 4 ) O(N^{\frac{1}{4}}) O(N41​),好看又好写。

代码

开了O2在luogu跑了600+ms,快的飞起,不开也有1.44s了,这不就快的飞起了吗。

可以试试这个跑不跑得出结果46856248255981。

强伪素数呀,都跑过去了。

#include<cstdio>
#include<cstring>
#include<cmath>
using  namespace  std;
typedef  long  double  LD;
typedef  long  long  LL;
inline  LL  zabs(LL  x){return  x<0?-x:x;}
inline  LL  mymin(LL  x,LL  y){return  x<y?x:y;}
inline  LL  mymax(LL  x,LL  y){return  x>y?x:y;}
inline  LL  ksc(LL  x,LL  y,LL  z)
{LL  c=(LD)x*y/z+0.5;LL  ans=x*y-c*z;return  ans<0?ans+z:ans;
}
inline  LL  ksm(LL  x,LL  m,LL  mod)
{if(m==0)return  1%mod;LL  ans=1;while(m>1){m&1?ans=ksc(ans,x,mod):0;x=ksc(x,x,mod);m>>=1;}return  ksc(ans,x,mod);
}
inline  int  log2(LL  &x)
{int  ans=0;while(x%2==0)ans++,x>>=1;return  ans;
}
int  su[]={2,3,5,7,11,23,29,61};
inline  bool  pd(LL  x)//判断一个素数
{for(int  i=0;i<=7;i++){if(x<=su[i])return  1;LL  y=x-1;int  tt=log2(y);y=ksm(su[i],y,x);while(tt--){LL  z=ksc(y,y,x);if(z==1  &&  y!=1  &&  y!=x-1)return  0;y=z;}if(y!=1)return  0;}return  1;
}
inline  LL  gcd(LL  x,LL  y)//实测二进制版GCD只比原来的快了20+ms,估计是因为优化减少了GCD的调用次数,凸显不出优势。
{int  ans=0;while(x  &&  y){if(x&1  &&  y&1){y>x?x^=y^=x^=y:0;x=(x-y)>>1;}else  if(x&1)y>>=1;else  if(y&1)x>>=1;else  x>>=1,y>>=1,ans++;}return  (x+y)<<ans;
}
inline  LL  Pol(LL  now,LL  step,LL  add)
{if(now%2==0)return  2;//防止毒瘤的4的情况 LL  x=2,y=2,d=1;while(1){LL  tx=x,ty=y;for(int  i=1;i<=step;i++){x=ksc(x,x,now)+add;x>=now?x-=now:0;y=ksc(y,y,now)+add;y>=now?y-=now:0;y=ksc(y,y,now)+add;y>=now?y-=now:0;d=ksc(d,zabs(x-y),now);}d=gcd(d,now);if(d==1)continue;else  if(d!=now)return  d;x=tx;y=ty;for(int  i=1;i<=step;i++){x=ksc(x,x,now)+add;x>=now?x-=now:0;y=ksc(y,y,now)+add;y>=now?y-=now:0;y=ksc(y,y,now)+add;y>=now?y-=now:0;d=gcd(zabs(x-y),now);if(d!=1)return  d%now;}}
}
inline  LL  work(LL  n)
{if(pd(n)  ||  n==1)return  n;LL  tmp=0,step=127/*玄学步数*/,add=1;while(!tmp)tmp=Pol(n,step,add++);//if(n/tmp<tmp)tmp=n/tmp;//使得n/tmp>=tmpLL  ans=work(n/tmp);if(ans>=tmp)return  ans;return  mymax(ans,work(tmp));//实现上的一个优化,优化空间小,但是能优化,而且不会耗多少空间,基本正优化
}
LL  n;
int  main()
{int  T;scanf("%d",&T);while(T--){scanf("%lld",&n);LL  ans=work(n);if(ans==n)printf("Prime\n");else  printf("%lld\n",ans);} return  0;
}

各种友(e)善(xin)数论总集,从入门到绝望2---快速判断素数相关推荐

  1. 各种友(e)善(xin)数论总集(未完待续),从入门到绝望

    目录 快速幂 扩展欧几里得 GCD 扩展欧几里得 同余系列 同余方程 同余方程组 一点想法 高次同余方程 BSGS exBSGS 线性筛素数 埃式筛 欧拉筛 欧拉函数 讲解 两道水题 法雷级数 可见点 ...

  2. 各种友(e)善(xin)数论总集,从入门到绝望2

    目录 前置芝士 二进制的GCD 快速乘 O(1) O(log) floyd提出的判环法 判环 找环 生日悖论 Miller_rabin 前言 二次探测 代码 Pollard-Rho 前言 例题 构建随 ...

  3. ACM数论之旅2---快速幂,快速求a^b

    如果题目说数据很大,还需要求余,那么代码就可以这么写 LL pow_mod(LL a, LL b){//a的b次方LL ret = 1;while(b != 0){if(b % 2 == 1){ret ...

  4. 数论的基础入门(初读数论概论有感)(acm知识储备)

    在寒假自己对自己的硬核知识进行了充电,这本书是学长极力推荐的,是专门给数学系的学生进行对数论进行全面认识和理解的入门书籍,虽然自己是计算机的,但是并不影响去阅读它,读的过程中就发现了,书中不少数学系的 ...

  5. 友盟+消息推送U-Push为无他相机 提供快速、高并发的推送服务

    在"颜值即正义"的今天,相机类App已经成为让人变美的刚需类应用工具.自拍分享.假日游玩.美食推荐......相机类App已经深入到用户的生活点滴.瘦脸,拉腿,换滤镜,加贴纸,一张 ...

  6. 数学--数论--Miller_Rabin判断素数

    ACM常用模板合集 #include<iostream> #include<algorithm> #include<cstring> #include<cst ...

  7. 数论 判断素数:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验

    普通的素数判断法 当我们要判断一个数字是否是素数的时候,往往会直接看这个数字模1到这个数字的根号,看有没有等于零的,从而判断这个数字是不是素数,这样做的时间复杂度为O(sqrt(n)) bool is ...

  8. `Computer-Algorithm` 数论基础知识 (同余,取模,快速幂,质数,互质,约数,质因子)

    catalog 同余 取模 快速幂 质数 互质 约数 质因子 @Delimiter(旧解释) 经验谈 两数之差也整除 加一的特殊性 取模 累加的周期性 取模的唯一集合 取模下的四则运算 除法的不可约性 ...

  9. 素数一套:Miller-Rabin 素性检验算法Pollard-Rho算法线性筛——Upside down primesDivisions

    部分目录 Solved 94 / 304 K Gym 100753K Upside down primes 高效判断素数 快速幂取模 继续 Miller-Rabin 素性检验算法 Unsolved 6 ...

最新文章

  1. 【Ubuntu】在Ubuntu中设置永久的DNS
  2. 洛谷P1433 吃奶酪【dfs】【剪枝】
  3. JVM-11虚拟机性能监控与故障处理工具之【JDK的可视化工具-JConsole】
  4. HTML5的基本入门格式介绍
  5. python为什么这么小_同样是 Python,怎么区别这么大
  6. 更适合Pythoner的标记语言Yaml学习总结
  7. 华南农业大学c语言上机实验答案,华南农业大学C语言上机实验答案.doc
  8. 微信蓝牙协议一:协议文档查阅方法和空中数据解析示例
  9. 树莓派 口罩识别 python_RaspberryPi上实现佩戴口罩识别——2020电赛F题小记
  10. 单片机c语言直接寻址 间接寻址,pic单片机教程之数据存储器的直接间接寻址方式...
  11. 安徽大学2020年计算机考研,2020年安徽大学计算机专业课初试科目调整
  12. 洛谷B2006 地球人口承载力估计
  13. 计算机专利英语笔译,基于Trados2014的专利翻译实践报告-英语笔译专业论文.docx...
  14. 使用easypoi操作excel
  15. exadata的exacli
  16. TunesKit Audio Converter for Mac(音频格式转换软件)
  17. 2020年10月25日总结
  18. 亿级短视频社交美拍架构实战
  19. 怎么搭建云平台的服务
  20. linux生成ssl证书脚本

热门文章

  1. Magisk搞机器记录(小米Mix3)
  2. 计算机 计算能力测试题,外貌年龄计算器测试题
  3. c语言指针flash,STM32F103RCT6之FLASH读写操作
  4. 深圳茁壮IPANEL浏览器中间件 debug模块移植参考,打印分级等功能,可以移到其他嵌入式系统
  5. 事件学习——1. 事件的认识
  6. 开集(Open Set)、闭集(Closed Set)和紧集(Compact Set)
  7. UA SIE545 优化理论基础1 例题4 证明一个集合是闭集
  8. 已解决WARNING: Ignoring invalid distribution -addlepaddle (d:softpython36libsite-packages)
  9. 位置不可用无法访问文件或目录损坏且无法读取
  10. 思维导图制作软件/网站