素数判断算法

素数判断算法

 

一. 朴素判断素数


1. 简单算法

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


2. 简单算法改进版

bool isprime (int n) {
	if (n < 2)	return false;
	if (n == 2)	return true;
	for (int i=3; i*i<n; i+=2)
		if (n%i == 0)	return false;
	return true;
}


时间复杂度均为o(根号n),对于一般素数判断,可以直接使用

 

二. 筛选法


筛素数的基本方法是用来筛选出一定范围内的素数,基本原理是利用素数p只有1p这两个约数,并且一个数的约数一定不大于本身,其过程:

把从1开始的、某一范围内的正整数从小到大顺序排列,1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。以此类推,直至筛子为空时结束。

 

1. 简单版

#define MAX 10001
bool isprime[MAX];
void TheSieveofEeatosthees() {
	int i, j;
	for (i=2; i<MAX; ++i)
		isprime[i] = true;
	for (i=2; i<MAX; ++i)
		if (isprime[i])
			for (j=i+i; j<MAX; j+=i)
				isprime[j] = false;
}

  isprime[i]true表示为素数,false表示为不是素数

  算法执行完后可以在的时间内判断出MAX以内的任意数是不是素数。筛选法的实际复杂度是log(log n)

 

2. 进阶版

#define MAX 10001
bool isprime[MAX] = {0};
int p[MAX] = {0};
void prime (int n) {
	int np=0, i, j;
	for (i=2; i<=n; i++) {
		if (!isprime[i])	p[np++] = i;
		for (j=0; j<np && p[j]*i<=n; j++) {
			isprime[p[j]*i] = true;
			if (i%p[j] == 0)	break;
		}
	}
}


  “if (i%p[j] == 0) break;”该句使得任何一个合数只能被它最小的质因数标记过一次,再一次进行优化。所以整个算法是线性的。但是空间复杂度非常巨大。

 

三. 费马素数测试


1. 费马小定理:

N为任意正整数,P为素数,且N不能被P整除(显然NP互质),

则有:

N^P%P = N (即:NP次方除以P的余数是N

公式变形为:

(N^(P-1))%P = 1

公式推理:

N^P%P = N

(N^P-N)%P = 0

(N*(N^(P-1)-1))%P = 0

因此N*(N^(P-1)-1)是NP的公倍数,又因为NP互质,所以

N*(N^(P-1)-1 = M*N*P (N*PNP的最小公倍数)

N^(P-1)-1 = M*P

M为整数,所以

(N^(P-1)-1)%P = 0

N^(P-1)%P = 1

 

2. 积模分解公式

如果有X%Z=0,则有(X+Y)%Z=Y%Z

设有XYZ三个正整数,则必有(X*Y)%Z=((X%Z)*(Y%Z))%Z

假设: X=Z*I+A   (1)

    Y=Z*J+B  (2)

(1)(2)式代入(X*Y)%Z得:((Z*I+A)*(Z*J+B))%Z

=(Z*(Z*I*J+I*B+J*A)+A*B)%Z

=(A*B)%Z

=((X%Z)*(Y%Z))%Z

 

3. 快速计算乘方的算法

若计算2^13,传统方法需要进行12次乘法。

若将2*2的结果保存起来:4*4*4*4*4*4*2

再把4*4的结果保存起来:16*16*16*2

一共5次运算,分别是2*24*416*16*2

unsigned Power (unsigned n, unsigned p) {	//计算n的p次方
	unsigned odd = 1;		//odd用来计算“剩下的”乘积
	while (p > 1) {			//一直计算到指数小于或等于1
		if ((p&1) != 0)		//判断p是否为奇数,偶数的最低位必为0
			odd *= n;		//若p为奇数,则把“剩下的”乘起来
		n *= n;				//主体乘方
		p /= 2;				//指数除以2
	}
	return n*odd;			//最后把主体和“剩下的”乘起来作为结果
}

4. 利用费马定理判断素数

算法思路:

对于N,从素数表中取出任意的素数对其进行费马测试,如果取了很多个素数,N仍未测试失败,则认为N是素数。测试次数越多越准确,一般50次足够了。

int Montgomery (int n, int p, int m) {		//蒙格马利快速幂模算法
	int k = 1;					//快速计算(n^e)%m的值,逐次平方法
	n %= m;
	while (p != 1) {
		if ((p&1) != 0) {
			k = (k*n)%m;
			n = (n*n)%m;
			p >>= 1;
		}
	}
	return (n*k)%m;
}
bool *isprime;
int *p;
int np = 0;
void prime(int n) {		//生成一个我们想要的小范围的素数表用于素数判断
	*isprime = (bool *)malloc(n*sizeof(bool));
	*p = (int *)malloc(n*sizeof(int));
	for (int i=2; i<=n; i++) {
		if (!isprime[i])	n[np++] = i;
		for (int j=0; j<np && p[j]*i<=n; j++) {
			isprime[p[j]*i] = 1;
			if (i%p[j] == 0)	break;
		}
	}
}
bool isprime(unsigned n) {
	if (n < 2)
		throw 0;
	for (int i=0; i<np; ++i)
		if (Montgomery(p[i],n-1,n))
			return false;
	return true;
}


费马定理是素数的必要条件而非充分条件,因此判断出的数不一定就是素数

 

四. 米勒拉宾素性测试

<span style="font-size:14px;font-weight: normal;">long long qpow(int a, int b, int r) {		//快速幂
	long long ans=1, buff=a;
	while (b) {
		if (b&1)		ans = (ans*buff)%r;
		buff = (buff*buff)%r;
		b>>=1;
	}
	return ans;
}
bool Miller_Rabbin(int n, int a) {	//米勒拉宾素数测试
	int r=0, s=n-1, j;
	if (!(n%a))		return false;
	while(!(s&1)) {
		s >>= 1;
		r++;
	}
	long long k = qpow(a, s, n);
	if (k == 1)
		return true;
	for (j=0; j<r; j++, k=k*k%n)
		if (k == n-1)
			return true;
	return false;
}
bool isprime(int n) {	//判断是否是素数
	int tab[] = {2, 3, 5, 7};
	for (int i=0; i<4; i++) {
		if (n == tab[i])
			return true;
		if (!Miller_Rabbin(n, tab[i]))
			return false;
	}
	return true;
}</span>


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值