素性测试是数论题中比较常用的一个技巧。它可以很基础,也可以很高级(哲学)。这次主要要介绍一下有关素数判断的奇技淫巧

素数的判断主要分为两种:范围筛选型&&单个判断型

我们先从范围筛选型这种常用的开始讲起,这里采用模板题Luogu P3383 【模板】线性筛素数来进行测试

1.埃氏筛

这是最常用的筛法了,思路也很简单:任何一个素数的倍数都是合数

然后我们O(n)扫一遍,同时筛去素数的倍数

但是有一些数如6,会被2和3都筛去一次,就造成了效率上的浪费,所以复杂度经证明为**O(n log log n)

CODE

#include<cstdio>
using namespace std;
const int N=10000005;
bool vis[N];
int n,m,x;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void get_prime(int m)
{
register int i,j;
for (vis[1]=1,i=2;i<=m;++i)
if (!vis[i]) for (j=i<<1;j<=m;j+=i) vis[j]=1;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
read(n); read(m); get_prime(n);
while (m--)
{
read(x);
puts(vis[x]?"No":"Yes");
}
return 0;
}

2.线性筛(欧拉筛)

这其实是对上者的优化,我们意识到一个数应该只有它的最小质因数删去,所以我们可以一边筛数的同时一边记录素数,这就是真正的O(n)复杂度

CODE

#include<cstdio>
using namespace std;
const int N=10000005;
int prime[N],n,m,x,cnt;
bool vis[N];
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void Euler(int n)
{
register int i,j;
for (vis[1]=1,i=2;i<=n;++i)
{
if (!vis[i]) prime[++cnt]=i;
for (j=1;j<=cnt&&i*prime[j]<=n;++j)
{
vis[i*prime[j]]=1;
if (!(i%prime[j])) break;
}
}
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
read(n); read(m);
Euler(n);
while (m--)
{
read(x);
puts(vis[x]?"No":"Yes");
}
return 0;
}

注意上面的那句话:

if (!(i%prime[j])) break;

这保证了线性筛的效率,不会产生重复,因为当i%prime[j]==0时这个数就是让后面的数删去。

3.基础素性测试

这是最基本的素数判定法了吧。从2到sqrt(x)枚举是否有数能够整除x

证明的话很简单,因为如果这个数是素数,那么它的因数必定为1和x,若其因数大于sqrt(x),那么平方后就大于x,这显然不可能。

所以我们O(sqrt(x))判断一次

CODE

#include<cstdio>
#include<cmath>
using namespace std;
int n,m,x;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline bool check(int x)
{
if (!(x^1)) return 0;
register int i; int bound=(int)sqrt(x);
for (i=2;i<=bound;++i)
if (!(x%i)) return 0;
return 1;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
read(n); read(m);
while (m--)
{
read(x);
puts(check(x)?"Yes":"No");
}
return 0;
}

4.对于算法3的优化

首先我们看一个结论:

大于等于5的质数一定和6的倍数相邻。

证明等参考:dalao's blog

然后同3,我们只不过每次快进6个单位,然后常数就得到了难以言喻都优化(一跃成为此题最快的算法)

CODE

#include<cstdio>
#include<cmath>
using namespace std;
int n,m,x;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline bool check(int x)
{
if (!(x^1)) return 0;
if (!(x^2)||!(x^3)) return 1;
if ((x%6)^1&&(x%6)^5) return 0;
register int i; int bound=(int)sqrt(x);
for (i=5;i<=bound;i+=6)
if (!(x%i)||!(x%(i+2))) return 0;
return 1;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
read(n); read(m);
while (m--)
{
read(x);
puts(check(x)?"Yes":"No");
}
return 0;
}

5.Miller-Rabin算法

这是历史上判断素数最快的方法了吧(但在此题中被算法4吊打了)

首先,这个算法基于费马小定理和二次探测定理:

二次探测定理:如果p是奇素数,则 x2≡1(modp)的解为x = 1或x = p - 1(mod p)

所以我们可以把x变成r*2^t的形式,其中r是一个奇数

然后我们结合两种算法&&快速幂就可以稳定O(log x)进行单次判断了

但是这个算法是一个非完美算法,它每一次都25%的概率是错的,所以我们可以多选择几个数多弄几次

但是偶然在网上看到一段话:

对于大数的素性判断,目前Miller-Rabin算法应用最广泛。一般底数仍然是随机选取,但当待测数不太大时,选择测试底数就有一些技巧了。比如,如果被测数小于4 759 123 141,那么只需要测试三个底数2, 7和61就足够了。当然,你测试的越多,正确的范围肯定也越大。如果你每次都用前7个素数(2, 3, 5, 7, 11, 13和17)进行测试,所有不超过341 550 071 728 320的数都是正确的。如果选用2, 3, 7, 61和24251作为底数,那么10^16内唯一的强伪素数为46 856 248 255 981。这样的一些结论使得Miller-Rabin算法在OI中非常实用。通常认为,Miller-Rabin素性测试的正确率可以令人接受,随机选取k个底数进行测试算法的失误率大概为4^(-k)。

所以对于这一题n=10000000的范围就只需要选择2,7,61即可

CODE

#include<cstdio>
using namespace std;
typedef long long LL;
const int prime[3]={2,7,61};
int n,m,x;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int quick_pow(int x,int p,int mod)
{
int tot=1;
while (p)
{
if (p&1) tot=((LL)tot*x)%mod;
x=((LL)x*x)%mod; p>>=1;
}
return tot;
}
inline bool Miller_Rabin(int x)
{
if (!(x^2)) return 1;
if (x<2||!(x&1)) return 0;
int t=0,u=x-1;
while (!(u&1)) ++t,u>>=1;
for (register int i=0;i<3;++i)
{
if (!(x^prime[i])) return 1;
if (!(x%prime[i])) return 0;
int lst=quick_pow(prime[i],u,x);
for (register int j=1;j<=t;++j)
{
int now=((LL)lst*lst)%x;
if (!(now^1)&&lst^1&&lst^(x-1)) return 0; lst=now;
}
if (lst^1) return 0;
}
return 1;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
read(n); read(m);
while (m--)
{
read(x);
puts(Miller_Rabin(x)?"Yes":"No");
}
return 0;
}

最后给出5个算法的运行结果(无O2)

  1. 埃氏筛

  2. 线性筛(欧拉筛)

  3. 基础素性测试

  4. 优化的算法3

  5. Miller-Rabin

有关素数判断的一些算法(总结&&对比)的更多相关文章

  1. POJ 1811 大素数判断

    数据范围很大,用米勒罗宾测试和Pollard_Rho法可以分解大数. 模板在代码中 O.O #include <iostream> #include <cstdio> #inc ...

  2. #C++初学记录(素数判断2)

    素数判断2 比较简单的算法,没有技术含量 A prime number is a natural number which has exactly two distinct natural numbe ...

  3. 数学:随机素数测试(Miller_Rabin算法)和求整数素因子(Pollard_rho算法)

    POJ1811 给一个大数,判断是否是素数,如果不是素数,打印出它的最小质因数 随机素数测试(Miller_Rabin算法) 求整数素因子(Pollard_rho算法) 科技题 #include< ...

  4. C语言 · 素数判断

     算法提高 素数判断   时间限制:1.0s   内存限制:512.0MB      编写一函数IsPrime,判断某个大于2的正整数是否为素数. 样例输入: 5样例输出:yes 样例输入: 9样例输 ...

  5. 实现100以内的素数输出(Python与C++对比)

    今天从链接http://www.2cto.com/kf/201302/187699.html中看到了Python实现100以内的素数输出的算法,颇受感触.尤其是被其中的Python的列表生成器的使用方 ...

  6. POJ3641 Pseudoprime numbers(快速幂+素数判断)

    POJ3641 Pseudoprime numbers p是Pseudoprime numbers的条件: p是合数,(p^a)%p=a;所以首先要进行素数判断,再快速幂. 此题是大白P122 Car ...

  7. JAVA语言的素数判断,随机数,函数调用

    近来刚学JAVA,就从JAVA写起吧,JAVA判别素数,其实方法和C/C++没什么区别,主要就是想谈一下,其中包括的3个点. (1)JAVA语言产生随机数,random函数,定义参数max的作用是给出 ...

  8. #C++初学记录(素数判断)

    练习题目二 素数判断 A prime number is a natural number which has exactly two distinct natural number divisors ...

  9. 2019-11-29-C#-字典-Dictionary-的-TryGetValue-与先判断-ContainsKey-然后-Get-的性能对比

    原文:2019-11-29-C#-字典-Dictionary-的-TryGetValue-与先判断-ContainsKey-然后-Get-的性能对比 title author date CreateT ...

随机推荐

  1. Kotlin入门(11)江湖绝技之特殊函数

    上一篇文章介绍了Kotlin对函数的输入参数所做的增强之处,其实函数这块Kotlin还有好些重大改进,集中体现在几类特殊函数,比如泛型函数.内联函数.扩展函数.尾递归函数.高阶函数等等,因此本篇文章就 ...

  2. 自己写bitmap

    备注:1.原创文章,转载请标明出处: 2.欢迎建议和意见 3.我的实现是C语言,为了保护公司隐私,下述数据类型被我改了.实际上int应改是无符号4个字节的类型,byte是有符号1个字节,才能保证移植性 ...

  3. 万能Makefile,前戏做足项目做起来才顺畅。

    # 获取要编译的源码 SRC :=$(wildcard *.cpp) OBJ :=$(patsubst %.cpp,%.o,$(SRC)) # 编译参数 CC :=g++ STD :=-std=c++ ...

  4. HTML 5 <input> list 属性

    定义和用法 list 属性引用数据列表,其中包含输入字段的预定义选项. 可以用来做关联搜素

  5. Scrapy爬取遇到的一点点问题

    学了大概一个月Scrapy,自己写了些东东,遇到很多问题,这几天心情也不大好,小媳妇人也不舒服,休假了,自己研究了很久,有些眉目了 利用scrapy 框架爬取慕课网的一些信息 步骤一:新建项目 scr ...

  6. 3.1Python数据处理篇之Numpy系列(一)---ndarray对象的属性与numpy的数据类型

    目录 目录 (一)简单的数组创建 1.numpy的介绍: 2.numpy的数组对象ndarray: 3.np.array(list/tuple)创建数组: (二)ndarray对象的属性 1.五个常用 ...

  7. zookeeper-03 命令行操作

    1. 前言 在3台机器分别部署了zookeeper-3.4.5,本文操作是在此基础上进行的.部署详情参见上一篇文章 2. 客户端登录与帮助查看 # 由于是集群模式,所以可以在3台机器的其中任意一台进行 ...

  8. Vs .Net Framework 灵活配置

    背景:我们开发和部署项目时都是通过注释某些配置项 比如: 在调试时就注释掉生产的配置项,在生产时又要改回来,只有一个还好,如果多的话就会非常容易出错. 问题1:在发布时容易出错,需要控制发布时根据配置 ...

  9. 罗马数字转整数的golang实现

    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I V X L C D M 例如, 罗马数字 2 写做 II ,即为两个并列的 1.12 写做 XII ,即为 X + ...

  10. 【Lucene4.8教程之中的一个】使用Lucene4.8进行索引及搜索的基本操作

    版权声明:本文为博主原创文章.转载请注明来自http://blog.csdn.net/jediael_lu/ https://blog.csdn.net/jediael_lu/article/deta ...