关于素数的基本介绍请参考百度百科here和维基百科here的介绍

首先介绍几条关于素数的基本定理:

定理1:如果n不是素数,则n至少有一个( 1, sqrt(n) ]范围内的的因子

定理2:如果n不是素数,则n至少有一个(1, sqrt(n) ]范围内的素数因子

定理3:定义f(n)为不大于n的素数的个数,则 f(n) 近似等于 n/ln(n) (ln为自然对数) ,具体请参考here


求不超过n的素数                         本文地址

算法1埃拉托斯特尼筛法,该算法的核心思想是:首先标记2~n的数都为素数,然后遍历2~n的数组,如果它是素数,就把它的倍数标记为非素数(即把所有素数的倍数都标记为非素数)代码如下:

 unsigned int findPrime(const unsigned int n, unsigned int prime[])
{
if(n < )return ;
unsigned int primeNum = ;
bool * isPrime = NULL;
//如果n太大,下面可能会申请内存失败
try{
isPrime = new bool[n+];//0表示是素数,1表示非素数
}catch(const std::bad_alloc &e){
std::cout<<"bad_alloc: new failed\n";
return -;
}
memset(isPrime, , sizeof(bool)*(n+));
for(long long i = ; i <= n; i++)
if(isPrime[i] == )
{//所有素数的倍数都不是素数
prime[primeNum++] = i;
long long t ;
for(unsigned int k = ; (t = i*k) <= n; k++)
isPrime[t] = ;
}
delete []isPrime;
return primeNum;//返回素数的个数
}

算法2:查表法,该算法主要根据定理2,如果一个数是素数,那么它不能被(1, sqrt(n) ]范围内的素数整除,称之为查表法,主要是因为当我们判断n是否为素数时,(1, sqrt(n) ]范围内的素数已经都计算出来了,可以利用已经计算出来的素数表,代码如下:


 unsigned int findPrime_table(const unsigned int n, unsigned int prime[])
{
if(n < )return ;
unsigned int primeNum = ;
prime[primeNum++] = ;
for(long long i = ; i <= n; i++)
{
unsigned int tmp = sqrt(i);
bool flag = true;
for(unsigned int j = ; prime[j] <= tmp; j++)
{
if(i % prime[j] == )
{
flag = false;
break;
}
}
if(flag)
prime[primeNum++] = i;
}
return primeNum;//返回素数的个数

对于两个算法的效率,我们分别计算不超过100,500,1000,10000,100000,1000000,10000000的素数,用时如下,左边是算法1的耗时,右边是算法2的耗时,单位微妙(测试环境,win7 x64 7G内存,i5处理器,codeblock with gcc):

1.65536,4.635

2.64857,16.2225

4.30393,26.1546

65.5521,313.524

607.847,5474.92

5904.66,74654.9

212453,1.38515e+006

通过上面的数据可知,算法1的性能优于算法2

在内存和时间允许的情况下,上面提供的代码(在int是32位的机器上)理论上能计算2^32-1以内的所有素数


素数的判定                      本文地址

算法1:最naive的方法,根据定理一来判断,代码如下:

 bool isPrime_naive(const unsigned int n)
{
if(n < )return false;
unsigned int k = sqrt(n);
for(unsigned int i = ; i <= k; i++)
if(n%i == )
return false;
return true;
}

算法2:根据定理2,先调用上面的素数生成算法生成sqrt(n)以内的素数(由于上面已经证明筛选法较快,因此下面判定算法中调用筛选法来生成素数表),然后再看他们是否都不能被n整除,代码如下:

bool isPrime_checkList(const unsigned int n)
{
if(n < )return false;
if(n == || n == ) return true;
unsigned int tmp = sqrt(n);
unsigned int *prime = new unsigned int[tmp+];
unsigned int primeNum = findPrime(tmp, prime);//生成sqrt(n)以内的素数
for(unsigned int i = ; prime[i] <= tmp && i < primeNum; i++)
if(n%prime[i] == )
{
delete []prime;
return false;
}
delete []prime;
return true;
}

算法3:概率算法,Miller Rabin 素数测试(参考资料:资料一资料二资料三),先讲几个有关的定理

费马小定理: 假如p是素数,且(a,p)=1,那么 a^(p-1) ≡1(mod p)。即:假如p是素数,且a,p互质,那么a的(p-1)次方除以p的余数恒等于1。

二次探测定理:若 p 为素数,且 0 < x < p,则方程 x * x = 1 ( mod p ) 的解为 x = 1, p - 1 ( mod p )

在以上两个定理的基础上得出Miller Rabin素数测试的理论基础:如果n是一个奇素数,将n - 1 分解成 ( 2^s ) * d,其中 s 是非负整数,d 是正奇数,a 是和n互素的任何整数,那么a^d≡1(mod n) 成立 或者 对某个r(0≤r ≤s -1) 等式 a^(2^r*d) ≡-1(mod n)成立

那么我们如何根据上述理论来测试某个数是否是素数呢,方法如下:对于一个数n, 将n - 1 分解成 ( 2^s ) * d,其中 s 是非负整数,d 是正奇数, 随机选取[1, n-1]内的整数a, 如果a^d≡1(mod n)  并且 对所有的r(0≤ r ≤s -1) a^(2^r*d) ≡-1(mod n),那么n是合数,否则n有3/4的概率是素数(关于3/4这个概率的证明见here

按照上述方法一次判断,把某个数误判成素数的概率1/4,但是不会把某个素数误判成合数,如果我们运行t次上述判断,那么误判的概率是 (1/4)^t, 当t = 10时这个概率是百万分之一,因此我们总结出miller rabin素数测试的算法步骤如下:

----------------------------------------------------------

算法输入:某个大于3的奇数n, 测试轮数t

算法循环t次下面的操作,只有当t次的输出结果都是素数时,该数就判定为素数,否则判定为合数

1、首先将n-1表示成(2^s)*d,其中s是非负整数,d是正奇数

2、在 [2, n-2] 内随机选择整数a,计算x = a^d mod n, 如果x = 1或n-1 ,返回 “是素数”,否则继续

3、i = [1, s-1]循环如下操作

  3.1,x = x^2 mod n

  3.2、如果x = 1,返回“是合数”

  3.3、如果x = n-1,返回“是素数”

4、返回“是合数”

注意:其中3.2的判断是基于以下定理:设x、y和n是整数,如果x^2=y^2 (mod n),但x ≠ ±y(mod n),则(x-y)和n的公约数中有n的非平凡因子. 3.2中如果 x =1, 即 a^(2^i * d) = 1(mod n) 由上述定理(y = 1)可知:若式子(ttt) a^(2^(i-1) * d) ±1(mod n) 成立,则a^(2^(i-1) * d) - 1 和n有非平凡因子,从而可判断n为合数。而上述式子(ttt)中a^(2^(i-1) * d)恰好是上一轮循环中的x,每次循环要继续的条件包括x不等于1和 n-1(mod n 情况下n-1 和 -1等价。x=1或n-1时函数返回了)。

----------------------------------------------------------

算法3代码如下,调用Miller_Rabin函数判断,默认是测试40次

 unsigned int exp_mod(unsigned int a, unsigned int b, unsigned int n)
{ //a^b mod n
unsigned long long ret = ;
unsigned long long a_copy = a;//防止大整数相乘溢出
for (; b; b>>=, a_copy = a_copy*a_copy%n)
if (b&)
ret = ret*a_copy%n;
return (unsigned int)ret;
} //n是否通过以a为基的测试M-R测试
//返回true时是素数,false是合数
bool witness(unsigned int a, unsigned int n)
{
unsigned int d = n-,s = ;
while((d&) == )
{//n-1 转换成 2^s * d
d >>= ;
s++;
}
unsigned int x = exp_mod(a, d, n);//a^d mod n
if(x == || x == n-)return true;
for(unsigned int i = ; i <= s-; i++)
{
x = exp_mod(x, , n);
if(x == )return false;
else if(x == n-)return true;
}
return false;
} bool Miller_Rabin(unsigned int n, int testTimes = )
{
if(n < )return false;
if(n == || n == )return true;//后面要生成[2,n-2]的随机数,因此2,3提前判断
if(n% == || n% == )return false;
srand(time(NULL));
for(int i = ; i <= testTimes; i++)
{
//产生[2,n-2]的随机数
unsigned int a = ((unsigned long long)rand())*(n-)/RAND_MAX + ;
if(witness(a, n) == false)
return false;
}
return true;
}

上述三个算法都可判断unsigned int 可表示的整数范围内的数,关于三个算法的效率:算法3效率是最高的,一般一个数在100~200微妙左右就能够判断,数越大,其优势越明显,总体而言速度是:算法3 > 算法2 > 算法1


下面提供一个计算某个区间[m,n]内的素数的函数,并把相应的素数写进一个txt文件,每行1000个,文件的最后一行写入该文件内素数的个数,文件以输入区间命名,如果只是想单纯计算个数,把相应的文件操作注释即可,经过计算2^32-1内的素数总共203280221个,如果有需要可以下载:不超过2^32-1的所有素数下载                 本文地址

 //生成[m,n]之间的素数,写进文件
unsigned int GeneratePrime(unsigned int m, unsigned int n)
{
if(n< || m>n)return ;
unsigned int primeNum = ;
unsigned int tmp = sqrt(n);
unsigned int *prime = new unsigned int[tmp+];
unsigned int k = findPrime(tmp, prime);//生成sqrt(n)以内的素数
char filename[];
sprintf(filename, "prime%u-%u.txt", m, n);
FILE *fp = fopen(filename, "w");
if(m < )m = ;
for(long long i = m; i <= n; i++)
{
unsigned int tmp = sqrt(i);
bool flag = true;
for(unsigned int j = ; prime[j] <= tmp && j < k; j++)
{
if(i % prime[j] == )
{
flag = false;
break;
}
}
if(flag)
{
fprintf(fp, "%lld ", i);
primeNum++;
if(primeNum % == )fprintf(fp, "\n");
}
}
fprintf(fp, "\nprime number:%d", primeNum);
fclose(fp);
delete []prime;
return primeNum;//返回素数的个数
}

本文所有代码下载 (备用地址

【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3398112.html

关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)的更多相关文章

  1. 与数论的厮守01:素数的测试——Miller Rabin

    看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...

  2. 用算法求N(N&gt;=3)之内素数的个数

    首先.我们谈一下素数的定义.什么是素数?除了1和它本身外,不能被其它自然数整除(除0以外)的数 称之为素数(质数):否则称为合数. 依据素数的定义,在解决问题上,一開始我想到的方法是从3到N之间每一个 ...

  3. 算法题:给你一个自然数N,求[6, N]之内的全部素数中, 两两之和为偶数的那些偶数。

    /* 算法题:给你一个自然数N,求[6, N]之内的全部素数中. 两两之和为偶数的那些偶数. */ #include <iostream> using namespace std; voi ...

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

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

  5. (Miller Rabin算法)判断一个数是否为素数

    1.约定 x%y为x取模y,即x除以y所得的余数,当x<y时,x%y=x,所有取模的运算对象都为整数. x^y表示x的y次方.乘方运算的优先级高于乘除和取模,加减的优先级最低. 见到x^y/z这 ...

  6. 【数论基础】素数判定和Miller Rabin算法

    判断正整数p是否是素数 方法一 朴素的判定   

  7. Miller Rabin素数检测与Pollard Rho算法

    一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...

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

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

  9. F - Goldbach`s Conjecture 对一个大于2的偶数n,找有多少种方法使两个素数的和为n;保证素数a<=b; a+b==n; a,b都为素数。

    /** 题目:F - Goldbach`s Conjecture 链接:https://vjudge.net/contest/154246#problem/F 题意:对一个大于2的偶数n,找有多少种方 ...

随机推荐

  1. Failed to load because no supported source was found

    Uncaught (in promise) DOMException: Failed to load because no supported source was found? 等待解决:

  2. Hadoop生态圈-Oozie部署实战

    Hadoop生态圈-Oozie部署实战 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Oozie简介 1>.什么是Oozie Oozie英文翻译为:驯象人.一个基于工作流 ...

  3. 使用JavaScript修改浏览器URL地址栏的实现代码【转】

    引用自http://www.jb51.net/article/42240.htm 现在的浏览器里,有一个十分有趣的功能,你可以在不刷新页面的情况下修改浏览器URL;在浏览过程中.你可以将浏览历史储存起 ...

  4. Every-SG游戏

    参考自 石家庄二中 贾志豪 IOI2009国家集训队论文 <组合游戏略述—— 浅谈 SG 游戏的若干拓展及变形> 一.定义 游戏规则加上 对于还没有结束的所有单一游戏,游戏者必须对其进行决 ...

  5. idea_2018.1.5版本的激活使用

    1:步骤help中找到register,然后按如下截图操作 注册码: K71U8DBPNE-eyJsaWNlbnNlSWQiOiJLNzFVOERCUE5FIiwibGljZW5zZWVOYW1lIj ...

  6. 05-迪米特法则(LOD 又名: 最少知道原则)

    1. 背景      类与类之间的关系越密切,耦合度越大,当一个类发生变化时,对另一个类的影响也越大. 2. 定义     一个类应该对其它类保持最少的了解. 3. 解决方法      尽量降低类与类 ...

  7. 自动升级CentOS Python至官方最新版

    #!/bin/bash # .检查当前系统Python版本 python_old_version=$(python -V >& | awk '{print $2}') echo &quo ...

  8. tabindex 带有指定 tab 键顺序 或焦点 focus

    登录注册时,文本框输入焦点 TAB 键时,自定义下一个焦点的顺序 <input type=" /> <input type=" /> 带有指定 tab 键顺 ...

  9. Linux 下安装 storm

    一:准备工作 (机器部署情况详见)这篇博客 3台安装supervisor,2台安装nimbus (1)安装jdk1.8 (2)安装zookeeper3.4.5 以上两部分安装可查看这篇博客 (3)下载 ...

  10. Java EE 之 Hibernate异常总结【4】org.hibernate.exception.SQLGrammarException: could not execute statement

    本质原因:配置的Java Bean,由Hibernate自动产生的SQL语句中有语法错误 原因如下: 情况1.存在字段名/表名与数据库关键字冲突 情况2.MySQL5.0以后与MySQL5.0以前事务 ...