길이가 네 번째 거듭 제곱 인 문자열 일치


28

이 질문의 범위 내에서 x임의의 횟수만큼 반복 되는 문자로 구성된 문자열 만 고려해 봅시다 .

예를 들면 다음과 같습니다.

<empty>
x
xx
xxxxxxxxxxxxxxxx

(실제로 반드시 그럴 필요는 없습니다 x-전체 문자열에 1 유형의 문자 만있는 한 어떤 문자도 괜찮습니다)

음수가 아닌 정수 n (n> = 0)에 대해 길이가 n 4 인 모든 문자열과 일치하도록 선택한 정규식 플레이버로 정규식을 작성하십시오 . 예를 들어 길이가 0, 1, 16, 81 등의 문자열이 유효합니다. 나머지는 유효하지 않습니다.

기술적 한계로 인해 128보다 큰 n 값은 테스트하기가 어렵습니다. 그러나 정규식은 논리적으로 올바르게 작동해야합니다.

정규 표현식에서 (Perl 사용자에게) 임의 코드를 실행할 수 없습니다. 다른 구문 (look-around, back-reference 등)이 허용됩니다.

문제에 대한 접근 방식에 대한 간단한 설명도 포함하십시오.

(자동 생성 된 정규식 구문 설명은 쓸모가 없으므로 붙여 넣지 마십시오)


"xx"는 유효하지 않습니까?
Kendall Frey

@ KendleFrey : 아뇨. 유효하지 않습니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh 당신은 그것에 대한 가능한 대답이 있다고 생각합니까?
xem

1
@Timwi : 예. Java, PCRE (아마도 Perl이지만 테스트 할 수 없음), .NET 그래도 Ruby / JS에서는 작동하지 않습니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
이 질문은 "고급 Regex-Fu"아래 의 스택 오버플로 정규 표현식 FAQ 에 추가되었습니다 .
aliteralmind

답변:


21

이 정규 표현식 은 효과가있는 것 같습니다.

^((?(1)((?(2)\2((?(3)\3((?(4)\4x{24}|x{60}))|x{50}))|x{15}))|x))*$

이 정규식은 PCRE, Perl, .NET 플레이버와 호환됩니다.

이것은 기본적으로 "차이 트리"(적절한 이름이 있는지 확실하지 않음)를 따르며, 정규 표현식에 다음 네 번째 거듭 제곱에 대해 더 많은 x가 일치하는지 알려줍니다.

1     16    81    256   625   1296  2401 ...
   15    65    175   369   671   1105 ...
      50    110   194   302   434 ...
         60    84    108   132 ...
            24    24    24 ...  # the differences level out to 24 on the 4th iteration

\2, \3,\4 상점과 각각 2, 3, 4 행과 같이 차이가 업데이트됩니다.

이 구성은 더 높은 전력을 위해 쉽게 확장 될 수 있습니다.

분명히 우아한 해결책은 아니지만 작동합니다.


+1. 좋은 대답입니다. 이 답변은 내 것과는 다르지만 (조건부 정규식을 사용하지만 내 정규식은 사용하지 않지만) 내 솔루션과 같은 정신을 가지고 있습니다 (차이 트리를 탐색하고 일부 정규식 엔진의 역 선언 된 역 참조를 사용하십시오).
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ 2012 년

깔끔한 아이디어 재차 트리. 사각형의 경우 나무는 1 4 9 16 ... 3 5 7 ... 2 2 2입니까?
Sparr

@Sparr 감사합니다, 예
변동성

24

다른 해결책

내 생각에 이것은 사이트에서 가장 흥미로운 문제 중 하나입니다. 데드 코드 를 맨 위로 올린 것에 대해 감사해야합니다 .

^((^|xx)(^|\3\4\4)(^|\4x{12})(^x|\1))*$

조건부 나 어설 션이없는 39 바이트 ... 사용중인 대체품 (^| )는 "첫 번째 반복"과 "첫 번째 반복이 아닌"중에서 선택하는 방식의 조건부 유형입니다.

이 정규식은 다음과 같이 작동합니다. http://regex101.com/r/qA5pK3/1

PCRE와 Python은 정규 표현식을 올바르게 해석하며 n 4 -1n 4 +1을 포함하여 Perl에서 n = 128 까지 테스트되었습니다 .


정의

일반적인 기술은 이미 게시 된 다른 솔루션과 동일합니다. 각 후속 반복에서 순방향 차이 함수 ( D f) 의 다음 항과 동일한 길이를 무제한 수량 자 ( *)로 일치시키는 자체 참조 표현식을 정의하십시오 . 전진 차분 함수의 공식적인 정의 :

정의 1 : 전진 차분 함수

또한 고차 차이 함수도 정의 할 수 있습니다.

정의 2 : 2 차 순차 함수

또는 더 일반적으로 :

정의 3 : k 차 순차 함수

전진 차분 함수에는 많은 흥미로운 속성이 있습니다. 그것은 미분이 연속 함수에 대한 것임을 시퀀싱하는 것이다. 예를 들어, n 차 다항식 의 D f 는 항상 n-1 차 다항식이며, 모든 i 에 대해 D f i = D f i + 1 이면 함수 f 는 지수와 거의 같습니다. e x 의 미분은 그 자체와 같습니다. f = D f2 n 인 가장 간단한 이산 함수입니다 .


f (n) = n 2

위의 해결책을 검토하기 전에 좀 더 쉬운 것으로 시작해 봅시다. 길이가 완벽한 정사각형 인 문자열과 일치하는 정규 표현식입니다. 전진 차분 함수 검토 :

FDF : n ^ 2

즉, 첫 번째 반복은 길이 1 의 문자열, 두 번째는 길이 3 의 문자열, 세 번째는 길이 5 의 문자열 등과 일치해야하며, 일반적으로 각 반복은 이전의 문자열보다 2 더 긴 문자열과 일치해야합니다. 해당 정규 표현식은이 문장에서 거의 직접적으로 따릅니다.

^(^x|\1xx)*$

첫 번째 반복은 하나만 x일치하고 이후의 각 반복은 지정된 것과 정확히 일치하는 이전 문자열보다 두 개 더 긴 문자열과 일치한다는 것을 알 수 있습니다 . 이것은 또한 perl에서 놀랍도록 짧은 완벽한 제곱 테스트를 의미합니다.

(1x$_)=~/^(^1|11\1)*$/

이 정규식은 n- gonal 길이 에 맞게 일반화 할 수 있습니다 .

삼각 숫자 :
^(^x|\1x{1})*$

제곱수 :
^(^x|\1x{2})*$

오각형 숫자 :
^(^x|\1x{3})*$

6 각 숫자 :
^(^x|\1x{4})*$

기타


f (n) = n 3

전진 차분 함수를 다시 검토하여 n 3으로 이동 합니다.

FDF : n ^ 3

이를 구현하는 방법이 즉시 명확하지 않을 수 있으므로 두 번째 차이 함수도 검사합니다.

FDF ^ 2 : n ^ 3

따라서 앞으로 차이 함수는 상수가 아니라 선형 값으로 증가합니다. D f 2 의 초기 ( ' -1 th') 값 이 0이므로 두 번째 반복에서 초기화를 저장하는 것이 좋습니다. 결과 정규식은 다음과 같습니다.

^((^|\2x{6})(^x|\1))*$

첫 번째 반복은 이전과 마찬가지로 1을 , 두 번째는 6을 더 긴 ( 7 ), 세 번째는 12를 더 긴 ( 19 ) 등과 일치시킵니다 .


f (n) = n 4

n 4 의 순차 차이 함수 :

FDF : n ^ 4

두 번째 전진 차분 함수 :

FDF ^ 2 : n ^ 4

세 번째 전진 차분 함수 :

FDF ^ 3 : n ^ 4

이제는 추악합니다. D f 2D f 3 의 초기 값은 각각 0이 아닌 2 , 12 이며 모두 계산해야합니다. 아마 정규식이 다음 패턴을 따를 것이라는 것을 알았을 것입니다.

^((^|\2\3{b})(^|\3x{a})(^x|\1))*$

때문에 D F 3 의 길이와 일치한다 (12)를 두 번째 반복에서, A는 필수적이다 (12) . 그러나 각 항이 24 씩 증가하므로 다음의 더 깊은 중첩은 이전 값을 두 번 사용해야하며 b = 2 입니다. 마지막으로해야 할 일은 D f 2를 초기화하는 것 입니다. D f 2는 D f에 직접 영향을 미치 므로 궁극적으로 일치시키려는 것이므로이 경우 적절한 원자를 정규식에 직접 삽입하여 값을 초기화 할 수 있습니다 (^|xx). 최종 정규 표현식은 다음과 같습니다.

^((^|xx)(^|\3\4{2})(^|\4x{12})(^x|\1))*$

더 높은 주문

5 차 다항식은 다음 정규식과 일치시킬 수 있습니다.
^((^|\2\3{c})(^|\3\4{b})(^|\4x{a})(^x|\1))*$

f (n) = n 5 는 두 번째와 네 번째 전차 함수의 초기 값이 0이므로 상당히 쉬운 운동입니다.

^((^|\2\3)(^|\3\4{4})(^|\4x{30})(^x|\1))*$

6 차 다항식의 경우 :
^((^|\2\3{d})(^|\3\4{c})(^|\4\5{b})(^|\5x{a})(^x|\1))*$

7 차 다항식의 경우 :
^((^|\2\3{e})(^|\3\4{d})(^|\4\5{c})(^|\5\6{b})(^|\6x{a})(^x|\1))*$

기타

필요한 계수가 정수가 아닌 경우 모든 다항식이 정확하게 이런 식으로 일치되는 것은 아닙니다. 예를 들어, n 6a = 60 , b = 8c = 3/2이어야 합니다. 이 경우이 문제를 해결할 수 있습니다.

^((^|xx)(^|\3\6\7{2})(^|\4\5)(^|\5\6{2})(^|\6\7{6})(^|\7x{60})(^x|\1))*$

여기에서 b6으로 , c2 로 변경했는데, 위에서 언급 한 값과 동일한 제품을 갖습니다. 이 제품으로 변경하지 않는 것이 중요 · B · C · ... 제어한다 6 차 다항식을위한 상수 차 함수, D f를 6 . 두 개의 초기화 원자가 있습니다. 하나 는 n 4 와 같이 D f2 로 초기화하고 다른 하나 는 b로 누락 된 두 개를 더하는 동시에 다섯 번째 차이 함수를 360 으로 초기화합니다 .


어떤 엔진을 테스트 했습니까?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

나는 무슨 일이 일어나고 있는지 마침내 이해합니다. 실제로 필요한 것은 순방향 참조에 대한 지원입니다. +1
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ 2013

@nhahtdh 아, 맞아. 순방향 참조는 반드시 보편적 인 기능 일 필요는 없습니다.
primo

1
우수한! 나는 이것이 짧고 간단하며 이해하기 쉽다는 것을 좋아합니다. 얕은 네 스팅을 사용하면 손으로 어떻게 동작하는지 쉽게 계산할 수 있습니다. 또한 Volatilitynhahtdh 의 솔루션 만큼 빠릅니다 . 그리고 나는 이것이 다항식으로 확장 될 수 있다는 데모를 포함하여 당신의 자세한 설명을 좋아합니다. 가능하다면 보너스 포인트를 줄 것입니다.
Deadcode

@Lynn 감사합니다! 기대하지 않았다 ...
primo

13

다음은 조건부, 앞으로 선언되거나 중첩 된 역 참조, 룩백, 밸런싱 그룹 또는 정규식 재귀를 사용하지 않는 솔루션입니다. 매우 광범위하게 지원되는 lookahead 및 표준 역 참조 만 사용합니다. Regex Golf 로 인해 이러한 제한 사항에 따라 운영하게되었습니다.ECMAScript 정규식 엔진을 사용하는 .

이 50 바이트 정규식이 작동하는 방식은 개념적으로 간단하지만이 퍼즐에 제출 된 다른 모든 솔루션과 완전히 다릅니다. 이런 종류의 수학적 논리가 정규식에서 표현 가능하다는 것을 발견 한 것은 놀라운 일이었습니다.

      \2                     \4  \5
^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+)$)\5){4})*x?$

(캡처 그룹은 정규식 위에 표시되어 있습니다)

정규식 단순히 교체함으로써 모든 전원 일반화 될 수있다 4하여{4} 원하는 파워.

온라인으로 사용해보십시오!

현재 값을 나눌 수있는 소수의 가장 작은 4 제곱을 반복해서 나눠서 작동합니다. 각 단계의 몫은 항상 네 번째 거듭 제곱이므로 원래 값은 네 번째 거듭 제곱입니다. 1의 마지막 몫은 원래 값이 실제로 4 제곱임을 나타냅니다. 이것으로 일치가 완료됩니다. 0도 일치합니다.

먼저 게으른 캡처 그룹 \2을 사용하여 1보다 큰 숫자의 가장 작은 요소를 캡처합니다. 따라서이 요소는 소수입니다. 예를 들어 1296 (6 ^ 4)에서는 처음에 \2= 2를 캡처 합니다.

그리고, 4 회 반복 루프의 처음에, 현재의 개수로 나누어 지 어떤지를 판정 \2하여, (?=\2+$). 이 루프를 통해 처음으로이 테스트는 쓸모가 없지만 나중에 그 목적이 분명해질 것입니다.

이 내부 루프에서 다음은 탐욕스러운 캡처 그룹 \4을 사용하여 자신보다 작은 숫자의 가장 큰 요소를 캡처합니다 (?=(x+)(\4+)$). 실제로 이것은 숫자를 가장 작은 소수로 나눕니다 \2. 예를 들어 1296은 처음에 \4= 1296/2 = 648 로 캡처됩니다 . 현재 숫자를\2 은 암시 적입니다. 현재 숫자를 캡처 그룹에 포함 된 숫자로 명시 적으로 나눌 수는 있지만 (이 답변을 게시 한 후 4 일 만에 발견했습니다) 그렇게하면 느리고 이해하기 어려운 정규 표현식을 만들 수 있습니다. 1보다 큰 숫자의 가장 작은 요소는 항상 자신보다 작은 가장 큰 요소와 일치하기 때문에 (제품이 숫자 자체와 같도록) 필요합니다.

이런 종류의 정규 표현식은 문자열의 끝에 결과를 남겨서 문자열에서 "먹을"수 있습니다 (작게 만들기). 문자열의 끝으로 나누기 결과를 "이동"해야합니다. 뺄셈 결과 (현재 숫자 빼기 \4)를 캡처 그룹으로 캡처 \5한 다음 룩어 헤드 외부에서에 해당하는 현재 숫자의 시작 부분을 일치시켜 수행합니다 \5. 이렇게하면 처리되지 않은 나머지 문자열 \4은 길이가 일치하는 끝에 남습니다 .

이제 내부 루프의 시작 부분으로 되돌아갑니다. 여기서 주요 요인으로 나눌 수있는 테스트가있는 이유가 분명해집니다. 우리는 숫자의 가장 작은 소수로 나눈 것입니다. 숫자가 여전히 해당 요소로 나눌 수있는 경우 원래 숫자는 해당 요소의 네 번째 거듭 제곱으로 나눌 수 있음을 의미합니다. 이 테스트를 처음 수행하면 쓸모가 없지만 다음 3 번은 묵시적으로 나눈 결과 \2가 여전히으로 나눌 수 있는지 판별합니다 \2. \2루프의 각 반복 시작 부분에서 여전히 나눌 수 있으면 각 반복이 숫자를으로 나눈 것 \2입니다.

이 예에서 입력이 1296 인 경우 다음과 같이 반복됩니다.

\2= 2
\4= 1296/2 = 648
\4= 648/2 = 324
\4= 324/2 = 162
\4= 162/2 = 81

이제 정규식이 첫 단계로 되돌아 갈 수 있습니다. 이것이 최종 결과 *입니다. 이 예에서 81은 새로운 숫자가됩니다. 다음 루프는 다음과 같이 진행됩니다.

\2= 3
\4= 81/3 = 27
\4= 27/3 = 9
\4= 9/3 = 3
\4= 3/3 = 1

이제 1을 새로운 숫자로하여 다시 첫 번째 단계로 되돌아갑니다.

숫자 1은 소수로 나눌 수 없으므로로 일치하지 (?=(xx+?)\2+$)않으므로 최상위 루프 ( *끝에 있는 루프 )를 종료합니다. 이제는x?$ . 이것은 0 또는 1 과만 일치 할 수 있습니다. 이 시점의 현재 숫자는 원래 숫자가 완벽한 4 제곱 인 경우에만 0 또는 1입니다. 이 시점에서 0이면 최상위 루프가 아무 것도 일치하지 않았 음을 의미하며, 1이면 상위 루프가 더 이상 나눌 수 없을 때까지 4 차 전력을 완전히 나눈 것을 의미합니다. 처음에는 1이었고 최상위 루프는 아무것도 일치하지 않습니다.)

반복적 인 명시 적 나누기를 수행하여 이것을 49 바이트로 해결할 수도 있습니다 (모든 전력에 대해 일반화됩니다-원하는 전력에서 1을 뺀 값으로 대체하십시오 {3}).이 방법은 훨씬 느리고 속도가 빠르며 사용하는 알고리즘에 대한 설명 이 답변의 범위를 벗어납니다.

^((x+)((\2(x+))(?=(\4*)\2*$)\4*(?=\5$\6)){3})?x?$

온라인으로 사용해보십시오!


내 테스트 (길이 1024까지)에서 올바른 것으로 보입니다. 그러나 정규 표현식이 너무 느리므로 길이 16 ^ 4와 일치하는 데 많은 시간이 걸리므로 많은 수를 확인하기가 매우 어렵습니다. 그러나 성능이 필요하지 않으므로 정규식을 이해하면 찬성합니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ 2013

1
정규 표현식과 휘발성은 훌륭합니다. 그들의 속도와 간결함이 나를 놀라게합니다. 둘 다 i7-2600k에서 7.5 초 만에 100000000에 달합니다. 정규식보다 훨씬 빠릅니다. 내 솔루션은 50625와 일치하는 데 12 초가 걸리기 때문에 완전히 다른 차수에 있습니다. 그러나 내 목표는 속도가 아니라 훨씬 제한된 작업 세트를 사용하여 최소 코드 길이로 작업을 수행하는 것입니다.
Deadcode

역 추적을 거의하지 않기 때문에 우리의 답변은 빠릅니다. 귀하는에서 역 추적을 많이 ((((x+)\5+)\4+)\3+)\2+$합니다. 앞으로 선언 된 역 참조없이 제곱을 일치시키는 방법조차 생각할 수 없기 때문에 귀하의 방식도 놀랍습니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

그건 그렇고,이 질문은 코드 골프가 아니라 퍼즐입니다. 코드 길이로 솔루션을 판단하지 않습니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

오. 왜 당신이 사용했는지 설명합니다 (?:). 최적화 된 버전을 기본 버전으로 만들려면 답변을 편집해야합니까?
Deadcode

8

해결책

^(?:(?=(^|(?<=^x)x|xx\1))(?=(^|\1\2))(^x|\3\2{12}xx))*$

이 정규식은 Java, Perl, PCRE 및 .NET 버전과 호환됩니다. 이 정규식은 미리 살펴보기, 뒤돌아보기 및 앞으로 선언 된 역 참조와 같은 다양한 기능을 사용합니다. 앞으로 선언 된 역 참조 종류는이 정규식의 호환성을 일부 엔진으로 제한합니다.

설명

이 솔루션은 다음 파생을 사용합니다.

합계를 완전히 확장하면 다음과 같은 평등을 증명할 수 있습니다.

\ sum \ limits_ {i = 1} ^ n (i + 1) ^ 4-\ sum \ limits_ {i = 1} ^ ni ^ 4 = (n + 1) ^ 4-1
\ sum \ limits_ {i = 1} ^ ni ^ 4-\ sum \ limits_ {i = 1} ^ n (i-1) ^ 4 = n ^ 4

왼쪽에 요약을 결합합시다.

\ sum \ limits_ {i = 1} ^ n (4 (i + 1) ^ 3-6 (i + 1) ^ 2 + 4 (i + 1)-1) = (n + 1) ^ 4-1
\ sum \ limits_ {i = 1} ^ n (4i ^ 3-6i ^ 2 + 4i-1) = n ^ 4

2 개의 방정식을 빼고 (상단 방정식-하단 방정식) 왼쪽에있는 합을 결합한 다음 단순화합니다.

\ sum \ limits_ {i = 1} ^ n (12i ^ 2 + 2) = (n + 1) ^ 4-n ^ 4-1

우리는 연속 제 4 거듭 제곱의 차이를 거듭 제곱으로 구합니다.

(n + 1) ^ 4-n ^ 4 = \ sum \ limits_ {i = 1} ^ n (12i ^ 2 + 2) + 1

이것은 연속 4 제곱 의 차이 가 (12n 2 + 2) 증가 함을 의미합니다 .

Volatility의 답변에서 차이 트리를 참조하면 생각하기가 더 쉽습니다 .

  • 최종 방정식의 오른쪽은 차이 트리의 두 번째 행입니다.
  • 증분 (12n 2 + 2)은 차이 트리의 세 번째 행입니다.

충분한 수학. 위의 솔루션으로 돌아 가기 :

  • 첫 번째 캡처 그룹은 일련의 홀수를 유지하여 i를 계산합니다. 은 방정식에 표시된대로 2 .

    정확하게 말하면, 루프가 반복됨에 따라 첫 번째 캡처 그룹의 길이는 0 (사용하지 않음), 1, 3, 5, 7, ...입니다.

    (?<=^x)x홀수 시리즈의 초기 값을 설정합니다. 이것 ^으로 첫 번째 반복에서 미리보기를 만족시킬 수 있습니다.

    xx\1 2를 더하고 다음 홀수로 옮깁니다.

  • 두 번째 캡처 그룹은 i 2 의 제곱 수 계열을 유지합니다 .

    정확하게 말하면, 루프가 반복됨에 따라 2 번째 캡처 그룹의 길이는 0, 1, 4, 9, ...입니다.

    ^in (^|\1\2)제곱 숫자 시리즈의 초기 값을 설정합니다. 그리고 \1\2홀수를 현재 제곱 수에 더하여 다음 제곱 수로 옮깁니다.

  • 세 번째 캡처 그룹 (앞을 보지 않고 실제로 텍스트를 소비 함)은 위에서 파생 한 방정식의 전체 오른쪽과 일치합니다.

    ^xin 은 방정식의 오른쪽에 (^x|\3\2{12}xx)있는 초기 값 을 설정합니다 + 1.

    \3\2{12}xx캡처 그룹 2에서 n 2 를 사용하여 차이 증가 (12n 2 + 2)를 더하고 동시에 차이를 일치시킵니다.

이러한 배열은 각각의 반복에서 매칭되는 텍스트의 양이 n 2 를 구성하기 위해 미리보기를 실행하는데 필요한 텍스트의 양 이상이기 때문에 가능하다 .

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