我在RosettaCode上找到了以下Java代码示例:
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
.?|(..+?)\\1+
如何匹配素数?
答案 0 :(得分:115)
您说您理解这一部分,但只是要强调,生成的字符串的长度等于提供的数字。因此,当且仅当n == 3
时,字符串有三个字符。
.?
正则表达式的第一部分说,“任何字符,零或一次”。所以基本上,有零个或一个字符 - 或者,根据我上面提到的n == 0 || n == 1
。如果我们有匹配,那么返回否定。这相当于零和一个不是素数的事实。
(..+?)\\1+
正则表达式的第二部分有点棘手,依赖于群组和反向引用。一个组是括号中的任何内容,然后由正则表达式引擎捕获并存储以供以后使用。反向引用是一个匹配的组,稍后将在同一个正则表达式中使用。
该组捕获1个字符,然后捕获任何字符中的1个或多个字符。 (+字符表示一个或多个,但仅限于前一个字符或组。所以这不是“两个或四个或六个等字符”,而是“两个或三个等”。+?类似+,但它尝试匹配尽可能少的字符。+如果可以的话,通常会尝试吞噬整个字符串,这在这种情况下很糟糕,因为它会阻止反向引用部分工作。)
下一部分是反向引用:同一组字符(两个或更多)再次出现。所述反向引用出现一次或多次。
因此。捕获的组对应于捕获的自然数量的字符(从2开始)。然后,所述组出现一些自然次数(也从2开始)。如果存在匹配,则意味着可以找到与n长度字符串匹配的两个大于或等于2的数字的乘积...意味着您有一个复合n。所以再次回到成功匹配的否定:n不是素数。
如果找不到匹配项,那么你就无法想出一个大于或等于2的两个自然数的乘积...并且你有一个不匹配和一个素数,因此再次返回否定比赛结果。
你现在看到了吗?这是令人难以置信的棘手(而且计算成本很高!)但是一旦你得到它,它同时也很简单。 : - )如果您有其他问题,我可以详细说明,例如正则表达式解析实际上是如何工作的。但我现在试图让这个答案变得简单(或者尽可能简单)。
答案 1 :(得分:71)
我将在素性测试之外解释正则表达式部分:以下正则表达式,给定String s
由重复String t
组成,找到t
。
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
它的工作方式是正则表达式将(.*)
捕获到\1
,然后查看是否有\1+
跟随它。使用^
和$
可确保匹配必须是整个字符串。
因此,在某种程度上,我们会获得String s
,这是String t
的“倍数”,正则表达式会找到t
(最长可能,因为{ {1}}贪婪)。
一旦你理解了这个正则表达式的工作原理,那么(忽略OP的正则表达式中的第一个替代)解释它如何用于素性测试很简单。
\1
的素数,请先生成长度为n
的{{1}}(填充相同的String
)n
长度char
捕获到String
,并尝试将k
与\1
的其余部分匹配
\1+
是String
的正确倍数,因此n
不是素数。k
这样的n
除以k
,因此n
是素数
n
如何匹配素数?
实际上,它没有! matches .?|(..+?)\1+
,其长度不是素数!
String
:替换的第一部分与长度为.?
或String
的{{1}}匹配(根据定义为非素数)0
:替换的第二部分,即上面解释的正则表达式的变体,匹配长度为1
的{{1}},(..+?)\1+
是String
的“倍数”长度为n
(即String
是复合而非素数)。
k >= 2
,但通过先尝试较小的n
请注意?
语句中的k
!
补码运算符:它会否定boolean
。当正则表达式没有匹配时,return
是素数!这是一个双重否定的逻辑,所以难怪它有点令人困惑!!
这是对代码的简单重写,使其更具可读性:
matches
以上内容基本上与原始Java代码相同,但分解为多个语句,并赋予局部变量赋值以使逻辑更易于理解。
我们还可以使用有限重复来简化正则表达式,如下所示:
n
同样,如果public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
长度为boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
,则填充相同的String
,
n
检查char
,不是素数.{0,1}
检查n = 0,1
是(.{2,})\1+
的正确倍数,不是素数除了n
上的不情愿的修饰符k >= 2
(为清晰起见省略),上述正则表达式与原始版本相同。
以下正则表达式使用类似的技术;它应该具有教育意义:
?
答案 2 :(得分:25)
很好的正则表达技巧(虽然非常低效)...... :)
正则表达式定义非素数如下:
当且仅当N <= 1 OR N可被某些K> 1整除时,N才是素数。
不是将N的简单数字表示传递给正则表达式引擎,而是使用由重复字符组成的长度 N序列。析取的第一部分检查N = 0或N = 1,并且第二部分使用反向引用来寻找除数K> 1。它强制正则表达式引擎找到一些非空的子序列,该子序列可以重复至少两次以形成序列。如果存在这样的子序列,则意味着它的长度除以N,因此N不是素数。
答案 3 :(得分:3)