素数测试
在介绍素数测试相关算法之前,先要引入欧拉定理和费马定理。
欧拉定理:对于任意的整数n>1,a^φ(n) ≡1(mod n)对所有的a∈Z*n都成立。
其中φ(n)为Z*n的规模。φ(n)=n∏(1-1/p).可以理解成初始有一张{0,1,…,n-1}的表,然后对每个能整除n的p,在表中删除p的倍数后剩下的数。Z*n定义为{2,3…n-1}中与n互质的元素。例如:Z*14={3,5,9,11,13},φ(14)=5。
我们考虑的一个已知元素a对模n的倍数,,考虑对模a的幂组成的序列,其中a∈Z* n:
a0,a1,a2,a3,……
模n.第i个数为ai mod n.
例如:对模7,3的幂为
i 0 1 2 3 4 5 6 7……
3^i % 7 1 3 2 6 4 5 1 3……
则Z*7中,<3>={1,3,2,6,4,5},则φ(7)=6同样可以得到<1>={1},<2>={1,2,4}.
由欧拉定理我们可以从而得出费马定理,他是欧拉定理的特殊表现。
费马定理:如果p是素数,则a^(p-1) ≡1(mod p)对所有的a∈Z*p都成立。
当p为素数时,他的因子只有p和1. 根据φ(n)=n∏(1-1/p):
φ(p)=p(1-1/p1)..(1-1/pn)=p-1
由欧拉定理即可验证费马定理的正确性。
对于素数测试有一种简便的方法,我们称之为试除法。就是我们用2到sqrt(n)分别去除n,每次的试除需要常数时间,最坏的情况需要的时间是⊙(sqrt(n)),n为长度。所以只有n很小或者n有小因子的时候该算法才能较快的执行。
下面介绍一种效率较高的算法,Miller-Rabin随机素数测试法。他是对于伪素数的改进。
所谓伪素数的定义为:Z+ n表示Z n中的非零元素:
Z+ n={1,2,…n-1}
如果n是一个合数,且满足 an-1≡1(mod n)则说n是基于a的伪素数。例如:341,561(基于2)。
根据费马定理如果p是素数,则ap-1 ≡1(mod p)对所有的a∈Z*p都成立。
我们可以思考,如果要判断一个数p是素数,那么我们是不是只要随机找一个a其中p>a>1,如果a^p-1 ≡1(mod p),那么p有可能是素数了。如果对所有的a∈Z*p都成立,那么p必然是素数。反过来若发现存在一个a∈Z*p 使得ap-1 !≡1(mod p)则p必然不是素数。我们的这个算法的思想就是使用随机的a去探测是否存在这样的一个a使得a^(p-1) !≡1(mod p),如果存在这样的a,那么就皆大欢喜,你已经证明了他不是素数了。如果没找到,那么他极有可能是素数了,但不一定是。
伪代码:
Pseudoprime(n)
1 if(an-1mod n!≡1)
2 return composite.
3 else return prime.
该过程如果结果为composite则可以认定n不为素数。如果结果返prime则极大可能为素数。如果判定为合数结果总是正确的。如果判定为素数的话,只有当n是基于a的伪素数才会出错。这个过程出错的几率相当的小,选取512位数,基于2的伪素数概率不到1/1020.在实际的操作中几乎永远不会出错。
Miller-Rabin随机素数测试法对伪素数测试过程进行了两方面的改进:
第一:他实验了数个随机选取的a进行试验。
第二:当计算每个模取幂的值时,注意在最后一组平方里是否发现了对模n来说1的非平方根。如果存在,终止并输出pomposite.
下面证明第二点:
定理:如果p是一个奇素数且e≥1.则方程
X2≡1(mod pe)
仅有两个解: X1=1和X2=-1。
由定理可以得出推论:如果对模n存在1的非平凡平方根,则n是合数。(平凡平方根为1或-1)。因此检测非平凡平方根的存在可以有效的判断n是合数。把它运用与Miller-Rabin素数测试过程。令n-1=2tu,其中t≥1且u是奇数;亦即,n-1的二进制表示是奇数u的二进制后面跟上t个零。例如n=13,n-1的二进制为1100,则t=2,u=3.因此,an-1≡(a u)2^t,所以可以通过先计算a u mod n,然后对结果连续平方t次来计算an-1 mod n。
伪代码:
WITNESS(a,n)
1 let n-1=2tu,where t≥1 and u is odd
2 x0=au mod n
3 for i←1 to t
4 do xi=x2 i-1 mod n
5 if xi=1 and x i-1≠1and x i-1≠n-1
6 then return true
7 if(xt≠1)
8 then return true
9 return false
该算法通过第2行计算x0=au mod n。3到6行对结果进行t次平方,计算出an-1 mod n.
5到6行进行非平凡平方根的检测,如发现非平凡平方根则证明是合数,返回true。第7到8行为检测an-1 mod n是否为1,如果发现不为1则不满足素数an-1 mod n≡1,证明n是合数返回true,若没发现能证明n是合数的证据,则将会运行到第9行结束返回false.
多次试验可以有效的降低错误率的发生。例如:当a=2时561是伪素数,他将被当做素数处理。而在a=7的情况下,a560≡241(mod 561)因此a=7可以证明561为合数。
伪代码:
Miller-Rabin(n,s)
1for i=1 to s
2 do a=RANDOM(1,n-1)
3 if WITNESS (a,n)
4 the return composite//确定为合数
5 return prime
第1到4行进行s次实验,取a为1到n-1之间的随机数。第三行进行判断是否存在一个值能证明n是合数,如果返回值为true说明找到了n为合数定的证据,n必然是合数。返回false找不到,则说明n极大可能是一个素数。
Miller Rabin是一个运气算法。他出错的概率与你选择的s的大小以及随机数a取到的值有关。只有当运气太差,在主循环s次迭代中,每一次都没能发现n为合数的证据时,才会出错,每次都错过发现证据的概率至多为2-s,在实际运行中出错的几率是相当低的。
附:
欧拉定理:对于任意的整数n>1,a^φ(n) ≡1(mod n)对所有的a∈Z*n都成立。
其中φ(n)为Z*n的规模。φ(n)=n∏(1-1/p).可以理解成初始有一张{0,1,…,n-1}的表,然后对每个能整除n的p,在表中删除p的倍数后剩下的数。Z*n定义为{2,3…n-1}中与n互质的元素。例如:Z*14={3,5,9,11,13},φ(14)=5。
我们考虑的一个已知元素a对模n的倍数,,考虑对模a的幂组成的序列,其中a∈Z* n:
a0,a1,a2,a3,……
模n.第i个数为ai mod n.
例如:对模7,3的幂为
i 0 1 2 3 4 5 6 7……
3^i % 7 1 3 2 6 4 5 1 3……
则Z*7中,<3>={1,3,2,6,4,5},则φ(7)=6同样可以得到<1>={1},<2>={1,2,4}.
由欧拉定理我们可以从而得出费马定理,他是欧拉定理的特殊表现。
费马定理:如果p是素数,则a^(p-1) ≡1(mod p)对所有的a∈Z*p都成立。
当p为素数时,他的因子只有p和1. 根据φ(n)=n∏(1-1/p):
φ(p)=p(1-1/p1)..(1-1/pn)=p-1
由欧拉定理即可验证费马定理的正确性。
对于素数测试有一种简便的方法,我们称之为试除法。就是我们用2到sqrt(n)分别去除n,每次的试除需要常数时间,最坏的情况需要的时间是⊙(sqrt(n)),n为长度。所以只有n很小或者n有小因子的时候该算法才能较快的执行。
下面介绍一种效率较高的算法,Miller-Rabin随机素数测试法。他是对于伪素数的改进。
所谓伪素数的定义为:Z+ n表示Z n中的非零元素:
Z+ n={1,2,…n-1}
如果n是一个合数,且满足 an-1≡1(mod n)则说n是基于a的伪素数。例如:341,561(基于2)。
根据费马定理如果p是素数,则ap-1 ≡1(mod p)对所有的a∈Z*p都成立。
我们可以思考,如果要判断一个数p是素数,那么我们是不是只要随机找一个a其中p>a>1,如果a^p-1 ≡1(mod p),那么p有可能是素数了。如果对所有的a∈Z*p都成立,那么p必然是素数。反过来若发现存在一个a∈Z*p 使得ap-1 !≡1(mod p)则p必然不是素数。我们的这个算法的思想就是使用随机的a去探测是否存在这样的一个a使得a^(p-1) !≡1(mod p),如果存在这样的a,那么就皆大欢喜,你已经证明了他不是素数了。如果没找到,那么他极有可能是素数了,但不一定是。
伪代码:
Pseudoprime(n)
1 if(an-1mod n!≡1)
2 return composite.
3 else return prime.
该过程如果结果为composite则可以认定n不为素数。如果结果返prime则极大可能为素数。如果判定为合数结果总是正确的。如果判定为素数的话,只有当n是基于a的伪素数才会出错。这个过程出错的几率相当的小,选取512位数,基于2的伪素数概率不到1/1020.在实际的操作中几乎永远不会出错。
Miller-Rabin随机素数测试法对伪素数测试过程进行了两方面的改进:
第一:他实验了数个随机选取的a进行试验。
第二:当计算每个模取幂的值时,注意在最后一组平方里是否发现了对模n来说1的非平方根。如果存在,终止并输出pomposite.
下面证明第二点:
定理:如果p是一个奇素数且e≥1.则方程
X2≡1(mod pe)
仅有两个解: X1=1和X2=-1。
由定理可以得出推论:如果对模n存在1的非平凡平方根,则n是合数。(平凡平方根为1或-1)。因此检测非平凡平方根的存在可以有效的判断n是合数。把它运用与Miller-Rabin素数测试过程。令n-1=2tu,其中t≥1且u是奇数;亦即,n-1的二进制表示是奇数u的二进制后面跟上t个零。例如n=13,n-1的二进制为1100,则t=2,u=3.因此,an-1≡(a u)2^t,所以可以通过先计算a u mod n,然后对结果连续平方t次来计算an-1 mod n。
伪代码:
WITNESS(a,n)
1 let n-1=2tu,where t≥1 and u is odd
2 x0=au mod n
3 for i←1 to t
4 do xi=x2 i-1 mod n
5 if xi=1 and x i-1≠1and x i-1≠n-1
6 then return true
7 if(xt≠1)
8 then return true
9 return false
该算法通过第2行计算x0=au mod n。3到6行对结果进行t次平方,计算出an-1 mod n.
5到6行进行非平凡平方根的检测,如发现非平凡平方根则证明是合数,返回true。第7到8行为检测an-1 mod n是否为1,如果发现不为1则不满足素数an-1 mod n≡1,证明n是合数返回true,若没发现能证明n是合数的证据,则将会运行到第9行结束返回false.
多次试验可以有效的降低错误率的发生。例如:当a=2时561是伪素数,他将被当做素数处理。而在a=7的情况下,a560≡241(mod 561)因此a=7可以证明561为合数。
伪代码:
Miller-Rabin(n,s)
1for i=1 to s
2 do a=RANDOM(1,n-1)
3 if WITNESS (a,n)
4 the return composite//确定为合数
5 return prime
第1到4行进行s次实验,取a为1到n-1之间的随机数。第三行进行判断是否存在一个值能证明n是合数,如果返回值为true说明找到了n为合数定的证据,n必然是合数。返回false找不到,则说明n极大可能是一个素数。
Miller Rabin是一个运气算法。他出错的概率与你选择的s的大小以及随机数a取到的值有关。只有当运气太差,在主循环s次迭代中,每一次都没能发现n为合数的证据时,才会出错,每次都错过发现证据的概率至多为2-s,在实际运行中出错的几率是相当低的。
附:

int witness(int a, int n) { int x, d=1, i = ceil(log(n - 1.0) / log(2.0)) - 1; for ( ; i >= 0; i--) { x = d; d = (d * d) % n; if (d==1 && x!=1 && x!=n-1) return 1; if (((n-1) & (1<<i)) > 0) d = (d * a) % n; } return (d == 1 ? 0 : 1); } int miller(int n, int s = 50) { if (n == 2) return 1; if ((n % 2) == 0) return 0; int j, a; for (j = 0; j < s; j++) { a = rand() * (n-2) / RAND_MAX + 1; // rand()只能随机产生[0, RAND_MAX)内的整数 // 而且这个RAND_MAX只有32768直接%n的话永远也产生不了 // [RAND-MAX, n)之间的数 if (witness(a, n)) return 0; } return 1; }