在有限的时间内,计算出一个尽可能大的素数
一.问题点
- 有限时间:在一个可接受的时间范围内,并非依靠暴力求解
- 尽可能大:可计算素数的上限
- 素数:因数只有1和它本身的自然数
二.素数的生成
n 位的十进制位的大素数生成步骤如下:
- 产生一个 n 位的随机数p
- 若最低位为偶数, 则将它加1, 以确保该素数为奇数, 从而保证了平均节省一半的运算时间
- 检查以确保 p 不能被任何小素数整除, 如
3, 5, 7, 11
等等,目的是排除 p 是合数的绝大部分可能性, 减少了下面步骤对 p 进行素数测试的总次数, 从而大大节省了运算时间。我采用了除2 以外(因已经检验过 p 的奇偶性)的所有小于 20 的素数, 来测试 p 对于它们的整除性 - 产生一个随机数 a, 对 a 进行
Rabin- Miller
测试,如果 p 通过测试, 则另外产生一个随 机数 a, 再重新进行测试选取较小的 a 值,以保证较快的运算速度,作 3次Rabin-Miller
测试(1次看起来已足够, 但为保证较高的精确性, 可以多做几次) - 如果 p 通过测试, 则 p 是素数,否则将原被测试数加 2, 得到一个新的数, 再对新数进行测试, 直到找到一个素数为止
三.Miller-Rabin 算法
Miller-Rabin
算法是一种基于概率的素性测试算法,因此该算法与一般的基于事实的真素性测试方法的区别是:存在一定的误判率。但是在对于某个较大的待测数字进行多次测试的情况下,误判率可以控制在可忽略范围内。Miller-Rabin
算法基于 Fermat
算法,属于后者的变形、改进。
首先引入 Fermat
定理:n是一个奇素数,a是任何整数(1 ≤ a ≤ n-1)
,则a^(n-1)≡1(mod n)
根据此定理可以知道,对于给定的待测数字 n,可以通过设定素性测试算法,计算 w = a^(n-1)%n
的结果来判定。
-
W!=1
,n一定不是素数; -
W==1
,n可能是素数;
再引入二次探测定理,如果 n 是一个整数,且 0<x<n
,则:x2%p=1
有: x= 1 / x = p-1
。若n是素数,则( n-1 )
一定是偶数,因此可令(n-1)=m*(2^q)
,其中m是正奇数( 若n是偶数,则上面的m*(2^q)
一定可以分解成一个正奇数乘以2的k次方的形式 ),q是非负整数。
借助 Fermat
定理,进行如下测试:a^(m)%n、a^(2m)%n、a^(4m)%n、a^(m*(2^q))%n
。进行这些测试的过程为:Miller-Rabin
测试。
误判率:若 n 是素数,a 是小于 n 的正整数,则 n 对以 a 为基的Miller 测试,结果为真。Miller 测试进行 k次,将合数当成素数处理的错误概率最多不会超过 4^(-k)。
四.Java程序实现
/**
* 题目8:计算一个尽可能大的素数
* 题目描述:在有限的时间内,计算出一个尽可能大的素数。
*/
package edu.sust;
import java.util.Random;
import java.util.Scanner;
public class PrimeNumber {
public static void main(String[] args) {
long startTime = System.currentTimeMillis(); //开始时间
Scanner scanner = new Scanner(System.in);
long executeTime;//程序执行时间
System.out.println("请输入程序运行的时间(毫秒为单位):");
executeTime = scanner.nextLong();
long endTime = System.currentTimeMillis(); //结束时间
int maxPrime = 2; //最大素数
while (endTime - startTime < executeTime) {
int num = getRandom();
if (num > maxPrime) {
if (MillerRabin(num, 3))
maxPrime = num;
}
endTime = System.currentTimeMillis();
}
System.out.println(maxPrime);
scanner.close();
}
public static int getRandom() {
Random random = new Random();
int num = random.nextInt(Integer.MAX_VALUE);
if ((num & 1) != 1) {
num++;//若最低位为偶数, 则将它加1, 以确保该素数为奇数
}
//检查以确保 p 不能被任何小素数整除
int[] primes = {3, 5, 7, 11, 13, 17, 19};
for (int i = 0; i < primes.length; i++) {
if (num % primes[i] == 0)
return -1;
}
return num;
}
/**
* 利用费马小定理求大数幂取模
*
* @param a 底数
* @param b 指数
* @param n 模数
* @return 返回(a ^ b) mod n的值
*/
public static int FermatPower(int a, int b, int n) {
int result = 1;
while (b > 0) {
if ((b & 1) == 1)
result = (result * a) % n;
if ((a * a) % n == 1 && a != 1 && a != n - 1)
return -1;// 二次探测
b >>= 1;
a = (a * a) % n;
}
return result;
}
/**
* Miller-Rabin素性测试
*
* @param n 待测随机数
* @param time 检测次数
* @return 若通过测试,返回true,否则返回false
*/
public static boolean MillerRabin(int n, int time) {
Random random = new Random();
for (int i = 0; i < time; i++) {
if (FermatPower(random.nextInt(n - 1) + 2, n - 1, n) != 1)
return false;
}
return true;
}
}
参考文献
- 刘少涛, 凌捷. 数据加密算法与大素数的生成及运算[J]. 广东工业大学学报, 2001(04):27-31.
- 张宏, 刘晓霞, 张若岩. RSA公钥密码体制中安全大素数的生成[J]. 计算机技术与发展, 2008(09):137-139+143.
- 韩了了, 傅兴华, 刘新华, et al. 一种基于RSA公钥密码体制大素数的生成方法[J]. 贵州大学学报(自然科学版), 2005(04):101-104.