如何确定一个数字是否是正则表达式的素数?

时间:2010-05-08 17:58:34

标签: java regex primes

我在RosettaCode上找到了以下Java代码示例:

public static boolean prime(int n) {
  return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
  • 我不特别了解Java,但除了正则表达式本身之外,了解此代码段的所有方面
  • 我在内置PHP函数中找到了基本的高级Regex知识

.?|(..+?)\\1+如何匹配素数?

4 个答案:

答案 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)

/^1?$|^(11+?)\1+$/

转换为基数1后应用于数字(1 = 1,2 = 11,3 = 111,...)。非素数将与此匹配。如果它不匹配,那就是素数。

解释here