What would be the fastest method to test for primality in Java?
我正在尝试找到最快的方法来检查给定数字是否为质数(在Java中)。 以下是我想到的几种素数测试方法。 有没有比第二个实现(isPrime2)更好的方法?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | public class Prime { public static boolean isPrime1(int n) { if (n <= 1) { return false; } if (n == 2) { return true; } for (int i = 2; i <= Math.sqrt(n) + 1; i++) { if (n % i == 0) { return false; } } return true; } public static boolean isPrime2(int n) { if (n <= 1) { return false; } if (n == 2) { return true; } if (n % 2 == 0) { return false; } for (int i = 3; i <= Math.sqrt(n) + 1; i = i + 2) { if (n % i == 0) { return false; } } return true; } } public class PrimeTest { public PrimeTest() { } @Test public void testIsPrime() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Prime prime = new Prime(); TreeMap<Long, String> methodMap = new TreeMap<Long, String>(); for (Method method : Prime.class.getDeclaredMethods()) { long startTime = System.currentTimeMillis(); int primeCount = 0; for (int i = 0; i < 1000000; i++) { if ((Boolean) method.invoke(prime, i)) { primeCount++; } } long endTime = System.currentTimeMillis(); Assert.assertEquals(method.getName() +" failed", 78498, primeCount); methodMap.put(endTime - startTime, method.getName()); } for (Entry<Long, String> entry : methodMap.entrySet()) { System.out.println(entry.getValue() +"" + entry.getKey() +" Milli seconds"); } } } |
这是另一种方式:
1 2 3 4 5 6 7 8 9 10 | boolean isPrime(long n) { if(n < 2) return false; if(n == 2 || n == 3) return true; if(n%2 == 0 || n%3 == 0) return false; long sqrtN = (long)Math.sqrt(n)+1; for(long i = 6L; i <= sqrtN; i += 6) { if(n%(i-1) == 0 || n%(i+1) == 0) return false; } return true; } |
编辑
请注意,
不幸的是,我找不到声称
因此,我进行了一些测试。我创建了一个大小为
对于确定性5和10,
这是我的测试台:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | import java.math.BigInteger; import java.util.BitSet; public class Main { static BitSet primes; static boolean isPrime(int p) { return p > 0 && (p == 2 || (p%2 != 0 && primes.get(p/2))); } static void generatePrimesUpTo(int n) { primes = new BitSet(n/2); for(int i = 0; i < primes.size(); i++) { primes.set(i, true); } primes.set(0, false); int stop = (int)Math.sqrt(n) + 1; int percentageDone = 0, previousPercentageDone = 0; System.out.println("generating primes..."); long start = System.currentTimeMillis(); for(int i = 0; i <= stop; i++) { previousPercentageDone = percentageDone; percentageDone = (int)((i + 1.0) / (stop / 100.0)); if(percentageDone <= 100 && percentageDone != previousPercentageDone) { System.out.println(percentageDone +"%"); } if(primes.get(i)) { int number = (i * 2) + 1; for(int p = number * 2; p < n; p += number) { if(p < 0) break; // overflow if(p%2 == 0) continue; primes.set(p/2, false); } } } long elapsed = System.currentTimeMillis() - start; System.out.println("finished generating primes ~" + (elapsed/1000) +" seconds"); } private static void test(final int certainty, final int n) { int percentageDone = 0, previousPercentageDone = 0; long start = System.currentTimeMillis(); System.out.println("testing isProbablePrime(" + certainty +") from 1 to" + n); for(int i = 1; i < n; i++) { previousPercentageDone = percentageDone; percentageDone = (int)((i + 1.0) / (n / 100.0)); if(percentageDone <= 100 && percentageDone != previousPercentageDone) { System.out.println(percentageDone +"%"); } BigInteger bigInt = new BigInteger(String.valueOf(i)); boolean bigIntSays = bigInt.isProbablePrime(certainty); if(isPrime(i) != bigIntSays) { System.out.println("ERROR: isProbablePrime(" + certainty +") returns" + bigIntSays +" for i=" + i +" while it" + (isPrime(i) ?"is" :"isn't" ) + " a prime"); return; } } long elapsed = System.currentTimeMillis() - start; System.out.println("finished testing in ~" + ((elapsed/1000)/60) + " minutes, no false positive or false negative found for isProbablePrime(" + certainty +")"); } public static void main(String[] args) { int certainty = Integer.parseInt(args[0]); int n = Integer.MAX_VALUE; generatePrimesUpTo(n); test(certainty, n); } } |
我通过这样做来运行:
1 | java -Xmx1024m -cp . Main 15 |
在我的机器上,素数的生成花费了大约30秒。而对
这是最优雅的方式:
1 2 3 |
Java 1.4以上版本。无需进口。
太短了如此美丽。
看看AKS素数测试(及其各种优化)。它是在多项式时间内运行的确定性素数检验。
图宾根大学(德国)的Java中有此算法的实现。
您迈出了消除2的所有倍数的第一步。
但是,您为什么停在那里?您可以消除3以外的所有3的倍数,5以外的所有5的所有倍数,等等。
当遵循此推理得出结论时,您会得到Eratosthenes筛。
Jaeschke(1993)提出的快速测试是Miller-Rabin测试的确定性版本,在4,759,123,141以下,没有误报,因此可以应用于Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | // Given a positive number n, find the largest number m such // that 2^m divides n. private static int val2(int n) { int m = 0; if ((n&0xffff) == 0) { n >>= 16; m += 16; } if ((n&0xff) == 0) { n >>= 8; m += 8; } if ((n&0xf) == 0) { n >>= 4; m += 4; } if ((n&0x3) == 0) { n >>= 2; m += 2; } if (n > 1) { m++ } return m; } // For convenience, handle modular exponentiation via BigInteger. private static int modPow(int base, int exponent, int m) { BigInteger bigB = BigInteger.valueOf(base); BigInteger bigE = BigInteger.valueOf(exponent); BigInteger bigM = BigInteger.valueOf(m); BigInteger bigR = bigB.modPow(bigE, bigM); return bigR.intValue(); } // Basic implementation. private static boolean isStrongProbablePrime(int n, int base) { int s = val2(n-1); int d = modPow(b, n>>s, n); if (d == 1) { return true; } for (int i=1; i < s; i++) { if (d+1 == n) { return true; } d = d*d % n; } return d+1 == n; } public static boolean isPrime(int n) { if ((n&1) == 0) { return n == 2; } if (n < 9) { return n > 1; } return isStrongProbablePrime(n, 2) && isStrongProbablePrime(n, 7) && isStrongProbablePrime(n, 61); } |
这不适用于
这两项测试都比任何一种试验部门都要快得多。
如果您只是想查找一个数字是否为质数,那么它就足够了,但是如果您要查找从0到n的所有质数,则更好的选择是Eratosthenes筛
但这将取决于java在数组大小等方面的限制。
您的算法适用于相当小的数字。对于大数,应使用高级算法(例如,基于椭圆曲线)。另一个想法是使用一些"假素数"检验。这些将快速测试数字是否为质数,但它们并非100%准确。但是,与使用算法相比,它们可以帮助您更快地排除一些数字。
最后,尽管编译器可能会为您优化此操作,但您应该编写:
1 2 3 |
当然,有数百种素数测试,所有这些测试都基于数量的大小,特殊形式,因子大小等而具有各种优点和缺点。
但是,在Java中,我发现最有用的是:
1 |
它已经实现,并且非常快(我发现一个1000x1000的矩阵大约需要6秒钟,其中填充了0到2 ^ 64的长整数,确定为15),并且可能比我们凡人所能想到的更好。
它使用了Baillie–PSW素数测试的一个版本,尚无反例。 (尽管它可能会使用稍弱的测试版本,有时可能会出错。)
我认为这种方法是最好的。至少对于我来说-
1 2 3 4 5 6 7 8 9 10 11 | public static boolean isPrime(int num) { for (int i = 2; i<= num/i; i++) { if (num % i == 0) { return false; } } return num > 1; } |
根据您需要测试的数字的长度,您可以预先计算一个小数值(n <10 ^ 6)的质数列表,如果要求的数字在此范围内,则首先使用该质数列表。当然,这是最快的方法。 像其他答案中提到的那样,Eratosthenes筛是生成此类预计算列表的首选方法。
如果您的数字大于此数,则可以使用Rabin的素数检验。
拉宾素数检验
您所写的是大多数普通程序员所做的,并且在大多数时候应该足够了。
但是,如果您追求的是"最佳科学算法",则http://en.wikipedia.org/wiki/Prime_number中记录了许多变化(确定性程度不同)。
例如,如果您有70位数字,则JVM的物理限制可能会阻止代码运行,在这种情况下,您可以使用"筛子"等。
再次,就像我说的,如果这是编程问题还是软件使用的一般性问题,您的代码应该是完美的:)
我在这里优化了审判部门:
它返回一个布尔值。
还需要除isPrime(n)以外的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | static boolean[] smlprime = {false, false, true, true, false, true, false, true, false, false, false, true, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false}; public static boolean isPrime(long n) { //optimised if (n < 2) { return false; } if (n < smlprime.length) //less than smlprime.length do not need to be checked { return smlprime[(int) n]; //lol already checked } long[] dgt = longDigits(n); long ones = dgt[dgt.length - 1]; if (ones % 2 == 0) { return false; } if (ones == 0 || ones == 5) { return false; } if (digitadd(n) % 3 == 0) { return false; } if (n % 7 == 0) { return false; } if (Square(n)) { return false; } long hf = (long) Math.sqrt(n); for (long j = 11; j < hf; j = nextProbablePrime(j)) { //System.out.prlongln(Math.sqrt(i)); if (n % j == 0) { return false; } //System.out.prlongln("res"+res); } return true; } public static long nextProbablePrime(long n) { for (long i = n;; i++) { if (i % 2 != 0 && i % 3 != 0 && i % 7 != 0) { return i; } } } public static boolean Square(long n) { long root = (long) Math.sqrt(n); return root * root == n; } public static long[] longDigits(long n) { String[] a = Long.toString(n).split("(?!^)"); long[] out = new long[a.length]; for (int i = 0; i < a.length; i++) { out[i] = Long.parseLong(a[i]); } return out; } public static long digitadd(long n) { long[] dgts = longDigits(n); long ans = 0; for (long i : dgts) { ans += i; } return ans; } |
算法效率:O(n ^(1/2))算法
注意:下面的示例代码包含计数变量和用于打印结果的打印函数调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import java.util.*; class Primality{ private static void printStats(int count, int n, boolean isPrime) { System.err.println("Performed" + count +" checks, determined" + n + ( (isPrime) ?" is PRIME." :" is NOT PRIME." ) ); } /** * Improved O( n^(1/2)) ) Algorithm * Checks if n is divisible by 2 or any odd number from 3 to sqrt(n). * The only way to improve on this is to check if n is divisible by * all KNOWN PRIMES from 2 to sqrt(n). * * @param n An integer to be checked for primality. * @return true if n is prime, false if n is not prime. **/ public static boolean primeBest(int n){ int count = 0; // check lower boundaries on primality if( n == 2 ){ printStats(++count, n, true); return true; } // 1 is not prime, even numbers > 2 are not prime else if( n == 1 || (n & 1) == 0){ printStats(++count, n, false); return false; } double sqrtN = Math.sqrt(n); // Check for primality using odd numbers from 3 to sqrt(n) for(int i = 3; i <= sqrtN; i += 2){ count++; // n is not prime if it is evenly divisible by some 'i' in this range if( n % i == 0 ){ printStats(++count, n, false); return false; } } // n is prime printStats(++count, n, true); return true; } public static void main(String[] args) { Scanner scan = new Scanner(System.in); while(scan.hasNext()) { int n = scan.nextInt(); primeBest(n); System.out.println(); } scan.close(); } } |
输入素数2147483647时,将产生以下输出:
执行23170次检查,确定2147483647为PRIME。
在Intel Atom @ 1.60GHz,2GB RAM,32位操作系统上进行了测试
测试结果:
Long.MAX_VALUE = 9223372036854775807以下的最大质数是9223372036854775783
经过时间为171499毫秒或2分51秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class PrimalityTest { public static void main(String[] args) { long current_local_time = System.currentTimeMillis(); long long_number = 9223372036854775783L; long long_a; long long_b; if (long_number < 2) { System.out.println(long_number +" is not a prime number"); } else if (long_number < 4) { System.out.println(long_number +" is a prime number"); } else if (long_number % 2 == 0) { System.out.println(long_number +" is not a prime number and is divisible by 2"); } else { long_a = (long) (Math.ceil(Math.sqrt(long_number))); terminate_loop: { for (long_b = 3; long_b <= long_a; long_b += 2) { if (long_number % long_b == 0) { System.out.println(long_number +" is not a prime number and is divisible by" + long_b); break terminate_loop; } } System.out.println(long_number +" is a prime number"); } } System.out.println("elapsed time:" + (System.currentTimeMillis() - current_local_time) +" millisecond/s"); } } |