숫자가 정규 표현식의 소수인지 확인하는 방법은 무엇입니까?


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+")).
Gumbo

14
이것은 계산 비용이 많이들뿐만 아니라 잠재적으로 메모리가 비쌉니다. 소수를 찾는 알고리즘이 너무 간단하기 때문에 (세계에서는 왜 복잡하고 낭비하기 때문에)이 접근법을 사용하기로 결정했다면, "새로운 문자 [n] 이전에 점검을 수행해야한다 ] '을 (를) 사용하여 합리적인 임계 값 미만인지 확인하십시오. 예를 들어 "prime (Integer.MAX_VALUE)"를 호출 한 다음 OutOfMemoryError가 발생하면 버그를 신고하십시오.
nicerobot

28
@nicerobot : 밝게?
Cam

6
@nicerobot : 실제로, 나는 그것을 다시 가져갑니다. 나는 원래이 질문의 학문적 성격이 학습 목적으로 만 사용되었다는 것을 암시하고 당신이 불쾌한 멍청이라고 생각했습니다. 그러나 두 번째 생각에는 그렇지 않습니다. 정규 표현식이 학습 목적으로 만 사용된다는 질문에는 언급되거나 암시되지 않습니다. 실제로 첫 번째 인상은 코드 스 니펫이 진행되는 한 매우 단순 해 보이기 때문에 초보자는 실제로 사용할 수 있다고 가정 할 수 있습니다. +1.
Cam

7
@incrediman 걱정하지 마십시오. 어떻게 생각하는지 알 수 있습니다. 이것을 사용하는 결과에 대해 경고하고 그것이 어떻게 작동하는지 배우는 것을 방해하지 않는 것이 나의 의도였습니다. 간단한 "이것을 배포하지 마십시오." 내 의견의 나머지 부분에 앞서 귀하의 초기 관점에서 덜 들리게 들릴 수도 있습니다.
nicerobot

답변:


120

이 부분을 이해한다고 말했지만 강조하기 위해 생성 된 문자열의 길이는 제공된 수와 같습니다. 따라서 문자열에는 if 만있는 경우 3 개의 문자가 n == 3있습니다.

.?

정규식의 첫 부분은 "모든 문자, 0 번 또는 1 번"이라고 말합니다. 따라서 기본적으로 0 또는 1 문자가 있습니까? 또는 위에서 언급 한대로 n == 0 || n == 1. 우리가 일치하면 그 부정을 반환합니다. 이것은 0과 1이 소수가 아니라는 사실과 일치합니다.

(..+?)\\1+

정규식의 두 번째 부분은 그룹과 역 참조에 의존하는 조금 까다 롭습니다. 그룹은 괄호 안에있는 항목으로, 나중에 사용할 수 있도록 정규식 엔진에서 캡처하고 저장합니다. 역 참조는 나중에 동일한 정규식에서 사용되는 일치 그룹입니다.

그룹은 1 개의 문자를 캡처 한 다음 1 개 이상의 문자를 캡처합니다. (+ 문자는 하나 이상의 문자를 의미하지만 이전 문자 또는 그룹에만 해당됩니다. 따라서 "2 또는 4 또는 6 등 문자"가 아니라 "2 또는 3 등"입니다. +?는 +와 비슷하지만 +와 같습니다. 가능한 한 적은 수의 문자를 찾으려고합니다. + 일반적으로 가능한 경우 전체 문자열을 잡으려고 시도합니다.이 경우 역 참조 부분이 작동하지 않기 때문에이 경우에는 좋지 않습니다.)

다음 부분은 역 참조입니다. 동일한 문자 세트 (두 개 이상)가 다시 나타납니다. 상기 역 참조는 1 회 이상 나타난다.

그래서. 캡처 된 그룹은 캡처 된 자연 문자 수 (2 이상)에 해당합니다. 그런 다음이 그룹은 몇 번이나 자연스럽게 나타납니다 (2 이상). 일치하는 것이 있으면 n 길이 문자열과 일치하는 2보다 크거나 같은 두 숫자의 곱을 찾을 수 있음을 의미합니다. 이는 복합 n을 의미합니다. 다시 한 번, 성공적인 일치의 부정을 반환하십시오. n은 소수가 아닙니다.

일치하는 항목을 찾을 수 없으면 2보다 크거나 같은 두 개의 자연수를 가진 제품을 만들 수 없으며 불일치와 소수가 모두 있으므로 부정의 귀환 경기 결과.

이제 보입니까? 믿을 수 없을만큼 까다 롭고 계산 비용이 많이 듭니다.하지만 일단 얻으면 동시에 간단합니다. :-)

정규식 구문 분석이 실제로 어떻게 작동하는지와 같은 추가 질문이 있으면 자세히 설명 할 수 있습니다. 그러나 나는이 대답을 지금 당장 간단하게 유지하려고 노력하고 있습니다.


10
크롬 개발 콘솔에서 JS 로이 논리를 시도했습니다. 웹 페이지에서. 확인을 위해 5를 전달했습니다. 페이지가 추락했습니다!
Amogh Talpallikar

아래 설명은 더 나은 설명을 제공합니다. 계속하기 전에 읽어보세요!
Ivan Davidov

"더 나은"은 주관적이다-나는 그것이 다른 각도에서 문제에 접근한다고 말하고이 답변에 대한 훌륭한 보완책이다. :-)
Platinum Azure

1
실제로 이것을 자세히 설명하는 블로그 게시물을 작성했습니다 . 숫자가 소수인지 확인하는 정규 표현식 Demystifying .
Illya Gerasymchuk

73

주어진 다음 정규식 : 나는 소수성 테스트의 정규식 부분 밖에 설명 할 것이다 String s반복으로 구성 String t, 발견을 t.

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

작동 방법은 정규식 캡처입니다 (.*)\1, 다음이 있다면보고 \1+를 다음과 같습니다. 은 Using ^$보장하지만이 경기는 전체 문자열이어야.

그래서 어떤 식으로 우리 String s는의 "다수"인을 부여 받았으며 String t정규 표현식은 그런 것을 찾을 것입니다 t(가장 \1욕심이 많기 때문에 가능한 가장 길다 ).

이 정규식이 왜 작동하는지 이해하고 나면 우선 OP 테스트의 첫 번째 대안을 무시하고 우선 순위 테스트에 사용되는 방법을 간단하게 설명합니다.

  • 의 우선도를 테스트하려면 n먼저 String길이를 생성 n하십시오 (같은 것으로 채워짐 char)
  • 정규식 캡처는 String약간의 길이 (말의 k에) \1, 그리고 시도 일치하는 \1+의 나머지String
    • 일치하는 경우 n의 적절한 배수 k이며 따라서 n소수가 아닙니다.
    • 일치가 없습니다 경우, 그러한은 k그 분할을 존재하지 않는 n, 그리고 n따라서 소수

.?|(..+?)\1+소수 는 어떻게 일치합니까?

실제로는 그렇지 않습니다! 그것은 일치 String 길이가 NOT 소수!

  • .?: String길이 의 대체 일치 의 첫 번째 부분 0또는 1(정의에 의한 소수는 아님)
  • (..+?)\1+: 대체의 두 번째 부분, 위에서 설명한 정규 표현식의 변형은 String길이의 n"배" String길이 k >= 2(즉 n소수가 아닌 복합) 의 길이 와 일치 합니다.
    • reluctant 수정자는 ?실제로 정확성을 위해 필요하지는 않지만 더 작은 것을 k먼저 시도하여 프로세스 속도를 높이는 데 도움이 될 수 있습니다

명령문 의 ! boolean보수 연산자에 유의하십시오 . 정규식이 일치 하지 않을 때가 가장 중요합니다! 이중 음의 논리이므로 혼란 스럽습니다.returnmatchesn


단순화

다음은 코드를보다 읽기 쉽게 작성하기 위해 다시 작성하는 것입니다.

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 : 귀하의 접근 방식이 아마도 제 방법보다 낫다고 생각합니다. 내가 왜 그렇게 많은 투표 나 확인 표시를 받았는지 모르겠다. :-( 죄송합니다
Platinum Azure

@ 플래티넘 : 와우, 나는 당신이 공개적으로 그렇게 말하는 것을 결코 생각하지 않을 것입니다! 도와 주셔서 감사합니다. 어쩌면 나는 [Populist]이것에서 언젠가 는 얻을 것이다 .
polygenelubricants

2
글쎄, 그것은 단지 진실입니다 (내가 그것을 인식하는 것처럼) ...별로 큰 문제는 아닙니다. 나는 담당자를 위해 여기에 있지 않습니다 (항상 보너스와 즐거운 놀라움이지만). 나는 할 수있을 때 질문에 대답하려고합니다. 따라서 누군가 내가 특정 질문보다 더 잘했을 때 인정할 수 있다는 것은 놀라운 일이 아닙니다.
Platinum Azure

25

좋은 정규식 트릭 (매우 비효율적이지만) ... :)

정규 표현식은 비 프라임을 다음과 같이 정의합니다.

N <= 1 OR N이 K> 1로 나눌 수있는 경우에만 N이 소수가 아닙니다.

N의 간단한 디지털 표현을 정규식 엔진에 전달하는 대신 반복 문자로 구성된 길이 N 의 시퀀스가 ​​제공됩니다 . 분리의 첫 번째 부분은 N = 0 또는 N = 1을 확인하고 두 번째 부분은 역 참조를 사용하여 제수 K> 1을 찾습니다. 정규식 엔진이 시퀀스를 형성하기 위해 적어도 두 번 반복 될 수있는 비어 있지 않은 하위 시퀀스를 찾도록 강제합니다. 이러한 서브 시퀀스가 ​​존재하면, 길이가 N을 나누므로 N이 소수가 아님을 의미합니다.


2
이상하게도 더 길고 기술적 인 다른 설명을 반복해서 읽은 후에도 나는 설명 머릿속에서 '클릭'되는 것으로 밝혀졌다 .
8 비트 전문가

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

밑이 1로 변환 된 후 숫자에 적용됩니다 (1 = 1, 2 = 11, 3 = 111, ...). 비 프라임은 이것과 일치합니다. 일치하지 않으면 프라임입니다.

여기에 설명 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.