欢迎访问小站,阅读此文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质数测试算法,证明了可以在多项式时间内检验是否为素数。
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