考虑以下方法:
public static boolean isPrime(int n) {
return ! (new String(new char[n])).matches(".?|(..+?)\\1+");
}
我从来都不是正则表达式大师,所以任何人都可以完全解释这种方法实际上是如何工作的吗?此外,与确定整数是否为素数的其他可能方法相比,它是否有效?
首先,请注意,此正则表达式适用于一元计数系统中表示的数字,即
1 is 1
11 is 2
111 is 3
1111 is 4
11111 is 5
111111 is 6
1111111 is 7
等等。实际上,可以使用任何字符(因此.
表达式中的 s),但我将使用“1”。
其次,注意这个正则表达式匹配复合(非素数)数字;因此,否定检测素性。
解释:
表达式的前半部分,
.?
表示字符串 "" (0) 和 "1" (1) 是匹配的,即不是素数(根据定义,尽管可以争论。)
后半部分用简单的英语说:
匹配长度至少为 2 的最短字符串,例如 "11" (2)。现在,看看我们是否可以通过重复来匹配整个字符串。“1111”(4)匹配吗?“111111”(6) 匹配吗?“11111111”(8) 匹配吗?等等。如果不是,则再次尝试下一个最短的字符串“111”(3)。等等。
您现在可以看到,如果原始字符串不能与其子字符串的倍数匹配,那么根据定义,它是素数!
顺便说一句,非贪婪运算符?
是使“算法”从最短开始并向上计数的原因。
效率:
通过各种论点,这很有趣,但肯定不是有效的,其中一些我将在下面进行合并:
正如@TeddHopp 所指出的,众所周知的埃拉托色尼筛法不会费心检查整数的倍数,例如 4、6 和 9,在检查 2 和 3 的倍数时已经“访问过”。唉,这个正则表达式方法详尽地检查每个较小的整数。
正如@PetarMinchev 指出的那样,一旦我们达到数字的平方根,我们就可以“短路”多重检查方案。我们应该能够,因为大于平方根的因子必须与小于平方根的因子配对(否则两个大于平方根的因子会产生大于数字的乘积),如果存在这个更大的因子,那么我们应该已经遇到(并因此匹配)较小的因素。
正如@Jesper 和@Brian 简明扼要地指出的那样,从非算法的角度来看,考虑正则表达式如何通过分配内存来存储 string开始,例如 char[9000]
9000。嗯,这很容易,不是吗?;)
正如@Foon 所指出的,存在概率方法可能对更大的数字更有效,尽管它们可能并不总是正确的(改为使用伪素数)。但也有确定性测试 100% 准确,并且比基于筛分的方法更有效。Wolfram's有一个很好的总结。
素数的一元特征以及它为什么起作用已经被讨论过了。所以这是一个使用传统方法和这种方法的测试:
public class Main {
public static void main(String[] args) {
long time = System.nanoTime();
for (int i = 2; i < 10000; i++) {
isPrimeOld(i);
}
time = System.nanoTime() - time;
System.out.println(time + " ns (" + time / 1000000 + " ms)");
time = System.nanoTime();
for (int i = 2; i < 10000; i++) {
isPrimeRegex(i);
}
time = System.nanoTime() - time;
System.out.println(time + " ns (" + time / 1000000 + " ms)");
System.out.println("Done");
}
public static boolean isPrimeRegex(int n) {
return !(new String(new char[n])).matches(".?|(..+?)\\1+");
}
public static boolean isPrimeOld(int n) {
if (n == 2)
return true;
if (n < 2)
return false;
if ((n & 1) == 0)
return false;
int limit = (int) Math.round(Math.sqrt(n));
for (int i = 3; i <= limit; i += 2) {
if (n % i == 0)
return false;
}
return true;
}
}
这个测试从 2 开始计算这个数字是否是 9,999 的素数。下面是它在相对强大的服务器上的输出:
8537795 ns (8 ms)
30842526146 ns (30842 ms)
Done
因此,一旦数字变得足够大,它的效率就会非常低。(对于高达 999,正则表达式的运行时间约为 400 毫秒。)对于小数字,它很快,但以传统方式生成高达 9,999 的素数仍然比以旧方式生成高达 99 的素数更快( 23 毫秒)。
这不是检查数字是否为素数的真正有效方法(它检查每个除数)。
一种有效的方法是检查除数高达sqrt(number)
. 这是如果你想确定一个数字是否是素数。否则,有更快的概率素性检查,但不是 100% 正确。