关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)
关于素数的基本介绍请参考百度百科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 测试)的更多相关文章
- 与数论的厮守01:素数的测试——Miller Rabin
看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...
- 用算法求N(N>=3)之内素数的个数
首先.我们谈一下素数的定义.什么是素数?除了1和它本身外,不能被其它自然数整除(除0以外)的数 称之为素数(质数):否则称为合数. 依据素数的定义,在解决问题上,一開始我想到的方法是从3到N之间每一个 ...
- 算法题:给你一个自然数N,求[6, N]之内的全部素数中, 两两之和为偶数的那些偶数。
/* 算法题:给你一个自然数N,求[6, N]之内的全部素数中. 两两之和为偶数的那些偶数. */ #include <iostream> using namespace std; voi ...
- POJ2429_GCD & LCM Inverse【Miller Rabin素数測试】【Pollar Rho整数分解】
GCD & LCM Inverse Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9756Accepted: 1819 ...
- (Miller Rabin算法)判断一个数是否为素数
1.约定 x%y为x取模y,即x除以y所得的余数,当x<y时,x%y=x,所有取模的运算对象都为整数. x^y表示x的y次方.乘方运算的优先级高于乘除和取模,加减的优先级最低. 见到x^y/z这 ...
- 【数论基础】素数判定和Miller Rabin算法
判断正整数p是否是素数 方法一 朴素的判定
- Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】
Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...
- 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,找有多少种方 ...
随机推荐
- SDL2.0 VLC ubuntu安装和黑屏问题
开发环境安装: 1,执行:"sudo apt-get build-dep libsdl1.2",确定依赖库都装全了. sdl2.0没有正式发布到ubuntu,使用下面方法安装: h ...
- eclipse复制工作空间配置
eclipse复制工作空间配置 eclipse复制工作空间配置 总结一下,复制工作空间配置步骤如下: 1 使用eclipse新建workspace. 2 将新建的workspace下的.metad ...
- NO.11 复制时勿忘其每个成分
1.Coping 函数应该确保复制对象内的"每一个成员变量",和调用合适的 "base class"构造函数(base class 某些成员往往是private ...
- Django多域名配置之Django-hosts插件的使用
使用场景: Django中有两个app,如果通过域名来访问,可以使用www.domain.com/a.www.domain.com/b来访问.这样就显得有点LowB了.如果我想通过a.domain.c ...
- 51job_selenium测试
Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...
- MySQL_DDL(不定时更新)
1. //1.创建数据库,并指定字符集为utf8 create database rocker_oa default character set utf8; //2.创建用户,并指定密码为‘root’ ...
- 错误提示 nginx: [emerg] unknown directive "gzip_static"
1.检查nginx配置文件错误提示如下: [root@server nginx]# /applications/nginx/sbin/nginx -t -c /applications/nginx/n ...
- Hive记录-配置远程连接(JAVA/beeline)
1.修改配置hive-site.xml hadoop core-site.xml限制---参考Hive记录-部署Hive环境 2.启动hadoop #sh /usr/app/hadoop/sbi ...
- Hbase记录-HBase客户端API
本章介绍用于对HBase表上执行CRUD操作的HBase Java客户端API. HBase是用Java编写的,并具有Java原生API.因此,它提供了编程访问数据操纵语言(DML). HBaseCo ...
- golang基础数据结构链表
链表 链表(Linked list),是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer). 每个节点包含下一个节点的地址,这样把所有的节点串起来了, ...