素数的判断--费马&米勒测试&AKS

欢迎访问小站,阅读此文http://www.yandong.org/archives/542

最简单的方式:遍历 2-n的根次方

/*
 *判断一个整数是否是素数
 *yandong www.yandong.org 
 *2013-6-11
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>

int is_prime1(int n)
{
	int mid = sqrt((double)n);/* 遍历到n的根次方就可以了*/
	if(n % 2 == 0)
		return 0;
	int i;
	for(i=3;i<= mid;i+=2)
		if(n % i == 0)
			return 0;
	return 1;

}

做点优化,先筛选一部分:比如筛出2,3,5等的倍数


int prime( int num)
{
    if (num==2||num==3||num==5) 
    return 1;
    unsigned long c=7;
    if (num%2==0||num%3==0||num%5==0||num==1) 
        return 0;
    int maxc = (int)(sqrt((double)num));
    while (c<=maxc)
    {
        if (num%c==0) 
           return 0;
        c+=4;
        if (num%c==0) 
           return 0;
        c+=2;
        if (num%c==0) 
           return 0;
        c+=4;
        if (num%c==0) 
           return 0;
        c+=2;
        if (num%c==0) 
           return 0;
        c+=4;
        if (num%c==0) 
           return 0;
        c+=6;
        if (num%c==0) 
           return 0;
        c+=2;
        if (num%c==0) 
           return 0;
        c+=6;
    }
    return 1;
}
进一步优化,对于比较小的素数直接以空间换时间:
int isPrime(int n)
{
	static int const smallPrimes[] = { 
		2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
		61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131,
		137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
		211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
		283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373,
		379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 
		461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557,
		563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
		643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
		739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
		829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 
		937, 941, 947, 953, 967, 971, 977, 983, 991, 997
	};
	static int  smallPrimesNo = sizeof(smallPrimes)/sizeof(smallPrimes[0]);
	int i ;
	for(i= 0; i < smallPrimesNo && smallPrimes[i] <= n; ++i)
	{
	  if(smallPrimes[i] == n)
	  {
		return 1;
	  }	   
	}
	//TODO: generic algorithim goes here
	return 0;
}


再一次优化,因为任何一个合数可以写成几个素数之积,所以可以用smallPrimes来先尝试是否能整除n


费马测试:

原理:

如果p为素数,则对任何小于p的正整数a有

a^(p−1)≡1(mod p)

根据基本数理逻辑,一个命题正确,当且仅当其逆否命题正确。所以费马定理蕴含了这样一个事实:如果某个小于p的正整数不符合上述公式,则p一定不是素数;
令人惊讶的是,费马定理的逆命题也“几乎正确”,也就是说如果所有小于p的正整数都符合上述公式,则p“几乎就是一个素数”。
当然,“几乎正确”就意味着有出错的可能,这个话题我们后续再来讨论。至少从目前来看,费马定理给我们提供了一条检测素数的方法。


int fermat_test(long  num) /*默认以2为底进行测试*/
{
	if( 1 == num) return 0;
	if( 2 == num) return 1;
	long long d = mpower(2, num-1, num);
	if( 1 == d) return 1;
	return 0;
}

int fermat_test1(long num)/*优化策略时,多次检测,随机产生底数,或者用一些小素数*/
{
	if( 1 == num) return 0;
	if( 2 == num) return 1;
	
	static int const smallPrimes[] = { 
		2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
		61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131,
		137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
		211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
		283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373,
		379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 
		461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557,
		563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
		643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
		739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
		829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 
		937, 941, 947, 953, 967, 971, 977, 983, 991, 997
	};
	static int  smallPrimesNo = sizeof(smallPrimes)/sizeof(smallPrimes[0]);
	
	long long d;
	int i=0;
	for(;i<smallPrimesNo;i++)
	{
		 d = mpower(2, num-1, num);
		 if( 1 != d) return 0;
	}
	return 1;
}


米勒-拉宾测试:

原理:

Miller-Rabin检测依赖以下定理:

如果p是素数,x是小于p的正整数,且x^2 = 1 mod p,则x要么为1,要么为p-1。

简单证明:如果x^2 = 1 mod p,则p整除x^2 - 1,即整除(x+1)(x-1),由于p是素数,所以p要么整除x+1,要么整除x-1,前者则x为p-1,后者则x为1。

以上定理说明,如果对于任意一个小于p的正整数x,发现1(模p)的非平凡平方根存在,则说明p是合数。

对于p-1,我们总可以将其表示为u*2^t,其中u是奇数,t是正整数。此时:

a^(p−1)=a^(u*2^t)=(a^u)^2^t

也就是可以通过先算出a^u,然后经过连续t次平方计算出a^(p-1),并且,在任意一次平方时发现了非平凡平方根,则断定p是合数。

int modPow(int a, int b, int m)/**同余幂的快速算法/
{
	int v = 1;
	for (int p = a % m; b > 0; b >>= 1, p = mpower(p, p, m))
	if (b & 1) v = mpower(v, p, m);
	return v;
}
bool witness(int a, int n)
{       /* n1/n2 即为原理中提到的奇数u */
	int n1 = n - 1, s2 = n1 & -n1, x = modPow(a, n1 / s2, n);
	if (x == 1 || x == n1) return 0;
	for (; s2 > 1; s2 >>= 1)
	{
		x = modMultiply(x, x, n);/*依次平方,如果有一次命中则为合数*/
		if (x == 1) return 1;
		if (x == n1) return 0;
	}
	return 1;
}
 
/* 随即函数,用于随即产生底数a */
int random(int high)
{
	return (int)(high * (rand() / (double)RAND_MAX));
}
/*k为检测次数,以降低误判*/
int rh(int n, int k)
{
	srand(time(NULL));
	if (n == 2 || n == 3) return 1;
	if (n < 2 || n % 2 == 0) return 0;
	while (k-- > 0) if (witness(random(n - 3) + 2, n)) return 0;
	return 1;
}

其中u的计算也可以这样

u = n-1; 
while((u&1)==0)
{
        u=(u>>1);
}



优化方案:

if n < 1,373,653, it is enough to test a = 2 and 3.
if n < 9,080,191, it is enough to test a = 31 and 73.
if n < 4,759,123,141, it is enough to test a = 2, 7, and 61.
if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11.

AKS方法

2002年,印度人M. Agrawal、N. Kayal以及N. Saxena提出了AKS质数测试算法,证明了可以在多项式时间内检验是否为素数。

0_1301278688y2g3

http://blog.csdn.net/xw13106209/article/details/6282861


链接参考:

http://www.cnblogs.com/skyivben/archive/2010/07/10/1775001.html

http://blog.csdn.net/yuanwenqun2/article/details/4650758

http://zh.wikipedia.org/wiki/%E5%9F%83%E6%8B

http://blog.csdn.net/pi9nc/article/details/8872876

http://blog.chinaunix.net/uid-21712186-id-1818141.html

http://www.nowamagic.net/librarys/veda/detail/2329

http://wenku.baidu.com/view/bad113f29e314332396893a1.html

http://blog.csdn.net/jokes000/article/details/7543612

http://blog.csdn.net/liukehua123/article/details/5482854


如果您喜欢这篇文章,欢迎分享订阅。转载请注明出处www.yandong.org


转载于:https://my.oschina.net/toyandong/blog/137155

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值