在Java中testing素数的最快方法是什么?
我试图find最快的方法来检查给定的数字是否是素数(在Java中)。 以下是我提出的几种素质testing方法。 有没有比第二个实现(isPrime2)更好的方法?
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 "); } } }
这是另一种方式:
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; }
BigInteger's isProbablePrime(...)
对于所有的32位int
是有效的。
编辑
请注意, isProbablePrime(certainty)
并不总是产生正确的答案。 当确定性偏低时,会产生误报,正如评论中提到的@ dimo414。
不幸的是,我找不到声明isProbablePrime(certainty)
的源对于所有(32位) int
都是有效的(给出足够的确定性!)。
所以我做了一些testing。 我创build了一个大小为Integer.MAX_VALUE/2
代表所有不均匀数字的BitSet
,并使用一个素筛来查找1..Integer.MAX_VALUE
范围内的所有素数。 然后, i=1..Integer.MAX_VALUE
从i=1..Integer.MAX_VALUE
循环来testing每个new BigInteger(String.valueOf(i)).isProbablePrime(certainty) == isPrime(i)
。
对于确定性5和10, isProbablePrime(...)
产生了沿线的误报。 但是用isProbablePrime(15)
,没有testing失败。
这是我的testing平台:
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); } }
我跑的是:
java -Xmx1024m -cp . Main 15
素数的产生在我的机器上花费了大约30秒。 所有i
在1..Integer.MAX_VALUE
的实际testing花了大约2小时15分钟。
这是最优雅的方式:
public static boolean isPrime(int n) { return !new String(new char[n]).matches(".?|(..+?)\\1+"); }
Java 1.4+。 没有import需要。
那么短。 如此美丽。
看看AKS素数testing (及其各种优化)。 这是一个在多项式时间运行的确定性素性testing。
这里有来自德国Tuebingen大学的 Javaalgorithm实现
你采取了消除2的所有倍数的第一步。
但是,你为什么在那里停下来呢? 你可以消除3除3之外的所有倍数,5除5之外的所有倍数等。
当你按照这个推论得出结论时,你会得到Eratosthenes的筛子 。
你的algorithm适用于相当小的数字。 对于大数字,应该使用高级algorithm(例如基于椭圆曲线)。 另一个想法是使用一些“pseuso-primes”testing。 这些将迅速testing一个数字是一个主要的,但他们不是100%准确的。 但是,它们可以帮助您比使用algorithm更快地排除一些数字。
最后,虽然编译器可能会为你优化这个,你应该写:
int max = (int) (Math.sqrt(n) + 1); for (int i = 3; i <= max; i = i + 2) { }
你写的是大多数普通程序员所做的,大部分时间应该是足够的。
但是,如果你是在“最好的科学algorithm”之后,有许多变化(具有不同程度的确定性)logging在http://en.wikipedia.org/wiki/Prime_number 。
例如,如果你有一个70位的数字,那么JVM的物理限制可能会阻止你的代码运行,在这种情况下,你可以使用“Sieves”等。
再次,就像我说的,如果这是一个编程问题或在软件中使用的一般问题,你的代码应该是完美的:)
如果你只是想find一个数字是否为素数就足够了,但如果你试图find从0到na的所有素数,那么select将是Eratosthenes的Sieve
但是这取决于java在数组大小等方面的限制。
根据需要testing的数字的长度,可以预先计算小数值(n <10 ^ 6)的素数列表,如果要求的数字在此范围内,则首先使用该列表。 这当然是最快的方法。 就像在其他答案中提到的那样,Eratosthenes筛选是生成这种预先计算清单的首选方法。
如果你的数字比这大,你可以使用拉宾的素性testing。 拉宾素数testing
我认为这种方法是最好的。 至less对于我来说-
public static boolean isPrime(int num) { for (int i = 2; i<= num/i; i++) { if (num % i == 0) { return false; } } return num > 1; }
Jaeschke(1993)的一个快速testing是Miller-Rabintesting的确定性版本,它没有低于4,759,123,141的误报,因此可以应用于Java int
s。
// 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); }
这对于long
variables不起作用,但是不同的testing做了:BPSWtesting没有高达2 ^ 64的反例。 这基本上包括一个2强的可能的主要testing像上面,然后是一个强大的卢卡斯testing,这有点复杂,但没有根本的不同。
这两个testing都比任何一个试验部门都快得多。
当然有数百个素数testing,根据数量的大小,特殊的forms,因素的大小等等,各有利弊。
然而,在Java中,我发现最有用的是这样的:
BigInteger.valueOf(long/int num).isProbablePrime(int certainty);
它已经实现了,而且速度相当快(我发现一个1000×1000的matrix需要6秒,长度为0-2 ^ 64,肯定是15),而且可能比我们凡人所能想到的更好。
它使用Baillie-PSW素数testing的一个版本,没有知道反例。 (尽pipe它可能会使用稍微弱一些的testing版本,有时可能会出错)
algorithm效率:O(n ^(1/2))algorithm
注意:下面的示例代码包含计数variables,并调用打印函数来打印结果:
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; } // Check for primality using odd numbers from 3 to sqrt(n) for(int i = 3; i <= Math.sqrt(n); 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(); } }
当input素数2147483647时,它会产生以下输出:
执行23170个检查,确定2147483647是PRIME。
我在这里优化了试用部门:它返回一个布尔值。 isPrime(n)以外的方法也是需要的。
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; }