【数学】关于素数的检验

来源:互联网 发布:店淘软件 编辑:程序博客网 时间:2024/05/16 14:01

最近看了一些关于素数的检验问题(伯努利试验),简单写一写,和大家分享一下。

定义:
一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,即因数中只含有1和它本身,则称其为素数,又叫质数,其他的数称为合数。

素数的分布:
定义π(n)为小于等于n的素数个数,有定理如下(素数定理):

limnπ(n)n/lnn=1

则我们可以近似认为在前n个正整数中素数的个数大约是 1/lnn 个。

暴力的检验方法:
如果我们想知道一个数n是否为素数,那么根据定义,显然我们可以从2到n-1枚举一遍,看是否是n的因数,如果有是的,则是合数,否则是素数。时间复杂度O(n)。

略微改进的暴力检验方法:
显然上一种方法实在是过于暴力,我们多做了不少无用功。其实只需要从2到 n 枚举即可。
证明:
对于一个数n,如果存在一个大于(等于) n 的数x能整除它(是它的因数),则一定存在一个小于(等于) n 的数y也能整除它(是它的因数),且x*y=n。
这样我们的时间复杂度降到了O( n)。

费马检验:
以上两种方法虽然可以保证检验的正确性,但效率低下,并不适用于大素数的检验,于是便有了费马检测。
费马检测的理论基础是由“史上最杰出的业余数学家”费马提出的费马小定理:

ap11(mod p),gcd(a,p)=1(a,p),p

这说明对于一个素数p,任意a∈{1,2,3…p-1},以上等式恒成立。而令人惊奇的是其逆定理也 几乎 成立,即对于一个数n,如果任意a∈{1,2,3,…n-1},都使得以上等式成立,那么这个数n 几乎 就是一个素数。
于是我们就可以根据此结论来得出一种对于大素数的检验方法:
选择一个底数a(a∈{1,2,3…n-1}),计算 ap1 在 mod p 意义下是否等于1,如果不等于则n一定是合数,如果不等于则认为其是素数。

bool judge(int a,int b){    int s=1,p=b+1;    while (b>0){        if (b&1) s=(s*a)%p;        b>>=1; a=(a*a)%p;    }    if (s==1) return true;    else return false;}void Fermat(int n){    if (judge(2,n-1)) printf("May be a prime\n");//可能是素数    else printf("Must be a composite\n");//一定是合数}

费马检测的时间复杂度是O(logn)的,但因为是“几乎成立”,所以有一些合数会被误判为素数,这些数x满足任意a∈{1,2,3…x-1}等式均成立,它们被称为Carmichael数,不过Carmichael数并不多,分布十分稀疏,有研究证明,在以2为底的情况下,512位整数中碰到这种数的概率是 1/1020,而在1024位整数中碰到的概率是 1/1041,判断失误的概率非常低,所以费马检测的正确性还是比较可靠的。

Miller-Rabin检验:
因为存在Carmichael数,所以单一的费马检验并不能完全满足人们的需求,于是便诞生了Miller-Rabin检验。
Miller-Rabin检验的理论基础:
如果n是素数,x为小于n的正整数,且 x2=1(mod n),则x是1或n-1。
证明:
因为 x2=1(mod n),所以n整除 x21,即整除(x+1)(x-1)。因为n为素数,所以n整除(x+1)或(x-1),n整除(x+1)时x=n-1,n整除(x-1)时x=1。
这就说明,对于一个数n,如果存在小于n的正整数x使得等式有非平凡平方根(1和n-1之外的数),则n一定是一个合数。
于是我们就有了具体的操作思路:
执行T轮,以增加其可靠度。
首先把n-1分解成 k2t,然后累乘计算 ak20,ak21...ak2t 判断是否成立即可。

int Pow(int a,int b,int p){    int s=1;    while (b>0){        if (b&1) s=(s*a)%p;        b>>=1; a=(a*a)%p;    }    return s;}bool judge(int n,int k,int t){    int a=rand()%(n-2)+2;    int y=Pow(a,k,n);    if (y!=1 && y!=n-1)        for (int j=1; j<=t-1 && y!=n-1; j++){            y=(y*y)%n;            if (y==1) return false;        }    if (y!=n-1) return false;    return true;}bool witness(int n,int T){    if (n<2 || n&1==0) return false;    if (n==2) return true;    int k=n-1,t=0; bool f;    for (; k%2==0; k>>=1,t++);    for (int i=1; i<=T; i++){        f=true;        if (!judge(n,k,t)){            f=false;            break;        }    }    return f;}void Miller_Rabin(int n){    int T=rand()%41+10;    if (witness(n,T)) printf("May be a prime\n");//可能是素数    else printf("Must be a composite\n");//一定是合数 }

显然Miller-Rabin的时间复杂度是O(Tlogn),判断比较可靠。

3 0
原创粉丝点击