如何确定数字是否为正则表达式的质数?


128

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

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

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


9
@Amir Rachum:!new String(new char[n]).matches(".?|(..+?)\\1+")等同于!((new String(new char[n])).matches(".?|(..+?)\\1+"))
浓汤

14
这不仅在计算上昂贵,而且还可能极大地破坏内存。如果有人选择使用这种方法,我建议您不要这样做,因为查找素数的算法非常简单(为什么世界上会使它复杂化并变得如此浪费),应在“新字符[n]之前进行检查”。 ]”以确保其低于合理的阈值。例如,调用“ prime(Integer.MAX_VALUE)”,然后在抛出OutOfMemoryError时提交一个错误。
nicerobot 2010年

28
@nicerobot:减轻?
凸轮

6
@nicerobot:实际上,我收回了。我最初认为此问题的学术性质意味着仅将其用于学习目的,并且您是一个令人讨厌的笨蛋。然而,经过深思熟虑,事实并非如此。正则表达式仅用于学习目的,从来没有提到甚至暗示。实际上,我对它的第一印象是,就代码片段而言,它看起来非常简单,因此初学者可能确实认为它可以在实践中使用。+1。
凸轮

7
@incrediman不用担心。我可以看到您的想法。我只是想警告使用此功能的后果,而不是阻止学习它的工作原理。一个简单的“请不要部署它”。在我其余的评论之前,从您最初的角度来看,这听起来可能没有那么高调。
nicerobot

Answers:


120

您说您了解这部分,但仅强调一下,生成的字符串的长度等于提供的数字。因此,当且仅当字符串包含三个字符n == 3

.?

正则表达式的第一部分说:“任何字符,零次或一次”。因此,基本上是零个字符还是一个字符-或,按照我上面提到的,n == 0 || n == 1。如果我们有匹配项,则返回该否定项。这与零和一不是质数这一事实相对应。

(..+?)\\1+

正则表达式的第二部分有些棘手,它依赖于组和反向引用。组是括号中的任何内容,然后由正则表达式引擎捕获并存储以供以后使用。反向引用是一个匹配的组,稍后将在同一正则表达式中使用。

该组捕获1个字符,然后捕获任何一个或多个字符。(+字符表示一个或多个,但仅表示前一个字符或组。因此,这不是“两个或四个或六个等字符”,而是“两个或三个等”。+?类似于+,但是它会尝试匹配尽可能少的字符。+通常会尝试对整个字符串进行吞噬,这在这种情况下很糟糕,因为这会阻止反向引用部分正常工作。)

下一部分是反向引用:同一组字符(两个或更多)再次出现。所述反向引用出现一次或多次。

所以。捕获的组对应于捕获的自然字符数(从2开始)。然后,所述组出现一些自然次数(也是从2开始)。如果存在匹配项,则意味着可以找到两个大于或等于2的数字的乘积,这些数字与n长度的字符串匹配……意味着您有一个复合n。再次返回成功匹配的否定:n不是素数。

如果找不到匹配项,那么您将无法得出两个大于或等于2的自然数的乘积...并且您同时具有不匹配项和质数,因此再次返回否定项比赛结果。

现在看到了吗?这真是令人难以置信的棘手(而且计算量很大!),但是一旦获得它,它同时也很简单。:-)

如果您还有其他问题,例如正则表达式解析的实际工作方式,我可以详细说明。但我现在尝试使这个答案保持简单(或尽可能简单)。


10
我在chrome开发人员控制台中使用JS尝试了这种逻辑。在网页上。刚刚通过5检查。页面崩溃了!
Amogh Talpallikar,

以下评论提供了更好的解释。在继续之前,请先阅读它!
伊万·戴维多夫

“更好”是主观的-我会说它从不同的角度解决问题,并且是对这个答案的完美补充。:-)
白金Azure

1
我实际上写了一篇博客文章,对它进行了更详细的解释:解密正则表达式以检查数字是否为素数
Illya Gerasymchuk '16

73

我将在素数测试之外解释regex部分:给定String s包含重复的的以下regex String t查找t

    System.out.println(
        "MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
    ); // prints "Mamamia"

它的工作方式是将正则表达式捕获(.*)到中\1,然后查看是否\1+跟随它。使用^$确保匹配必须是整个字符串。

因此,以某种方式,我们得到String s,它是的“倍数” String t,而正则表达式将找到它t(可能\1是最长的,因为是贪婪的)。

一旦您了解了此正则表达式的工作原理,然后(现在暂时忽略OP的正则表达式中的第一个替代方法),就很容易解释了它如何用于素数测试。

  • 要测试的素数n,请首先生成一个Stringlength n(用填充相同的char
  • 正则表达式将String一定长度(例如k)的a捕获到中\1,并尝试与\1+其余部分匹配String
    • 如果存在匹配项,则n是的适当倍数k,因此n不是素数。
    • 如果没有匹配,则没有这样的k存在分歧n,并n因此是素

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

实际上,事实并非如此!它匹配 String的长度不是素数!

  • .?String长度0或的交替匹配的第一部分1(根据定义,不能为素数)
  • (..+?)\1+:交替的第二部分,该正则表达式的变形例如上所述,匹配String长度的n是一个“倍数” String长度的k >= 2(即n是一个复合物,不是素)。
    • 请注意,不愿意修改?实际上是没有必要的正确性,但它可以通过尝试更小的帮助的提速过程k第一

注意语句中的! boolean补码运算符return:它使取反matches。这是正则表达式匹配的时候,n是最重要的!这是双重负逻辑,所以难怪它有点令人困惑!!


简化版

这是对代码的简单重写,以使其更具可读性:

public static boolean isPrime(int n) {
    String lengthN = new String(new char[n]);
    boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
    return !isNotPrimeN;
}

上面的代码与原始Java代码基本相同,但是分成多个语句并分配了局部变量,以使逻辑更易于理解。

我们还可以使用有限重复来简化正则表达式,如下所示:

boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");

同样,给定String长度为的n,用相同的填充char

  • .{0,1}检查是否n = 0,1,不是素数
  • (.{2,})\1+检查是否n为的适当倍数k >= 2,不是素数

与不愿改性剂的异常?\1(为清楚起见省略),上述正则表达式是相同的原件。


更有趣的正则表达式

以下正则表达式使用类似的技术;应该具有教育意义:

System.out.println(
    "OhMyGod=MyMyMyOhGodOhGodOhGod"
        .replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"

也可以看看


6
+1:我认为您的方法可能比我的方法更好。我不知道为什么我会得到如此多的赞誉或复选标记...您应该得到的更多。:-(对不起
白金Azure

@Platinum:哇,我从没想过你会公开说出来!感谢您的支持。也许我会从中得到[Populist]一天。
多基因润滑剂2010年

2
好吧,这只是事实(按照我的看法)……确实不算什么。我不是来这里代表的(尽管它总是奖金和惊喜)...我在这里尝试回答问题。因此,当某人做得比我在某个特定问题上做得更好时,我可以接受就不足为奇了。
白金Azure

25

不错的正则表达式技巧(尽管效率很低)... :)

正则表达式对非素数的定义如下:

当且仅当N <= 1或N被K> 1整除时,N不是素数。

代替将N的简单数字表示传递给正则表达式引擎,而是给它提供长度为 N 的序列,该序列由重复字符组成。析取的第一部分检查N = 0或N = 1,第二部分使用后向引用查找除数K> 1。它强制正则表达式引擎找到一些非空子序列,该子序列可以重复至少两次以形成序列。如果存在这样的子序列,则意味着其长度除以N,因此N不是素数。


2
奇怪的是,即使反复阅读了其他冗长且技术性更高的解释,我仍然认为这种解释使我感到“点击”。
八位大师

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

应用于转换为以1为底的数字(1 = 1,2 = 11,3 = 111,...)。非素数将与此匹配。如果不匹配,则为质数。

在这里解释。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.