가장 빠른 미니 플락 퀸


26

Mini-Flak은 , 및 작업이 허용되지 않는 Brain-Flak 언어 의 하위 집합입니다 . 엄밀히 말하면 다음 정규식 과 일치 해서는 안됩니다 .<><...>[]

.*(<|>|\[])

Mini-Flak은 Brain-Flak의 가장 작은 알려진 Turing 전체 하위 집합입니다.


얼마 전 나는 Mini-Flak 에서 Quine 을 만들 수 있었지만 우주의 일생 동안 달리기가 너무 느 렸습니다.

그래서 당신에게 나의 도전은 더 빠른 Quine을 만드는 것입니다.


채점

코드의 점수를 매기려면 코드 @cy끝에 플래그를 넣고 플래그를 사용하여 Ruby 인터프리터 에서 실행하십시오 ( 루비 인터프리터를 사용하여 온라인으로 시도하십시오 ) -d. 다음과 같이 점수가 STDERR에 인쇄되어야합니다.

@cy <score>

이것은 프로그램이 종료하기 전에 걸리는주기 수이며 실행간에 동일합니다. 각주기는 실행하는 데 거의 같은 시간이 걸리므로 점수는 프로그램을 실행하는 데 걸리는 시간과 직접적으로 관련되어야합니다.

Quine이 컴퓨터에서 합리적으로 작동하기에 너무 길면 직접주기 수를 계산할 수 있습니다.

사이클 수를 계산하는 것은 그리 어렵지 않습니다. 사이클 수는 모나드 실행 횟수에 2 배의 닐 라드 실행 횟수를 더한 것과 같습니다. 이것은 모든 nilad를 단일 문자로 바꾸고 총 문자 수를 계산하는 것과 같습니다.

점수 예

  • (()()()) 모나드 1 개와 닐 라드 3 개가 있으므로 5 점을 얻습니다.

  • (()()()){({}[()])} 루프는 6 개의 모나드와 2 개의 nilads 8을 포함하는 반면 첫 번째 부분은 이전과 같고 5를 나타 내기 때문에 29를 기록합니다. 루프는 3 번 실행되므로 점수는 3 번 계산됩니다. 1*5 + 3*8 = 29


요구 사항

당신의 프로그램은 ...

  • 2 바이트 이상이어야합니다.

  • -A플래그를 사용하여 Brain-Flak에서 실행될 때 소스 코드 인쇄

  • 정규식과 일치하지 않습니다 .*(<|>|\[])


  • 크레인 - 플랙의 통역 빠르게 루비 인터프리터에 비해 절대적으로하지만 몇 가지 기능이 부족하다. 먼저 Crane-Flak을 사용하여 코드를 테스트 한 다음 작동한다는 것을 알 때 루비 인터프리터에서 점수를 매길 것을 권장합니다. 또한 TIO에서 프로그램을 실행하지 않는 것이 좋습니다. TIO는 데스크탑 인터프리터보다 느릴뿐만 아니라 약 1 분 안에 시간 초과됩니다. TIO가 시간 초과되기 전에 프로그램을 실행하기에 충분한 점수를 얻지 못하면 매우 인상적입니다.

  • [(...)]{}(...)[{}]같이 동일하게 작동 <...>하지만, 제한된 소스 요구 사항을 아프게하지 않습니다

  • 당신은 체크 아웃 할 수있다 두뇌 플랙미니 플랙 이 문제를 접근하는 방법의 아이디어를 원하는 경우 Quines합니다.


1
"현재 최고"-> "현재 만"
HyperNeutrino

답변:


33

미니-플락, 6851113주기

프로그램 (문자 그대로)

대부분의 사람들은 Mini-Flak 퀴인이 인쇄 할 수없는 문자와 멀티 바이트 문자 (인코딩 관련)를 사용하기를 기대하지 않을 것입니다. 그러나이 quine과 quine의 크기 (102646 바이트로 UTF-8로 인코딩 된 93919 자)와 결합 된 인쇄 할 수없는 파일은 프로그램을이 포스트에 배치하기가 상당히 어렵습니다.

그러나 프로그램은 매우 반복적이므로 압축이 아주 좋습니다. 전체 프로그램을 문자 그대로 Stack Exchange에서 사용할 수 있도록 아래 축소 형 뒤에 숨겨진 전체 quine xxdgzip압축 버전의 가역적 16 진수 덤프가 있습니다.

(예, 너무 반복적이므로 압축 반복 볼 수도 있습니다 ).

문제는 "TIO에서 프로그램을 실행하지 않는 것이 좋습니다. TIO는 데스크탑 인터프리터보다 느릴뿐만 아니라 약 1 분 후에 시간이 초과 될 것입니다. 누군가가 실행할만큼 점수가 낮 으면 매우 인상적입니다. TIO가 타임 아웃되기 전에 그들의 프로그램을 시작했습니다. " 나는 그걸 할 수있어! Ruby 인터프리터를 사용하여 TIO에서 실행하는 데 약 20 초가 걸립니다. 온라인으로 사용해보십시오!

프로그램 (읽을 수 있음)

이제 컴퓨터가 읽을 수있는 프로그램 버전을 제공했습니다. 사람이 읽을 수있는 버전을 사용해 봅시다. quine을 구성하는 바이트를 코드 페이지 437 (높은 비트 세트가있는 경우) 또는 유니 코드 제어 그림 (ASCII 제어 코드 인 경우)으로 변환하고 공백을 추가했습니다 (기존의 모든 공백은 제어 그림으로 변환 됨) ), 구문을 사용하여 길이가 인코딩 «string×length»되고 일부 데이터가 많은 비트가 생략되었습니다.

␠
(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                («()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
                … much more data encoded the same way …
                («()×117»(«()×115»(«()×117»
                «000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
                )[({})(
                    ([({})]({}{}))
                    {
                        ((()[()]))
                    }{}
                    {
                        {
                            ({}(((({}())[()])))[{}()])
                        }{}
                        (({}))
                        ((()[()]))
                    }{}
                )]{}
                %Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
                ,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

( "거의 241"은 241 번째 사본에 후행이 누락 '되었지만 다른 240과 동일하기 때문입니다.)

설명

코멘트에 대해

가장 먼저 설명해야 할 것은 인쇄 할 수없는 캐릭터와 Mini-Flak 명령이 아닌 다른 정크는 무엇입니까? 퀴네에 주석을 추가하면 상황이 더 어려워 질 수 있지만 이것은 속도 경쟁 (크기 경쟁이 아님)으로, 주석이 프로그램의 속도를 해치지 않는다는 것을 의미합니다. 한편 Brain-Flak과 Mini-Flak은 스택의 내용을 표준 출력으로 덤프합니다. 스택에 포함되어 있는지 확인해야하는 경우프로그램의 명령을 구성하는 문자는 스택 청소주기를 소비해야합니다. 정크 스택 요소가 유효한 Brain-Flak 명령이 아니고 (Brain-Flak / Mini-Flak 폴리 글 로트 생성), 음이 아니거나 외부가 아닌 한 Brain-Flak은 대부분의 문자를 무시합니다. 유니 코드 범위에서 스택에 그대로두고 출력 할 수 있으며 같은 위치에 같은 문자를 프로그램에 배치하여 quine 속성을 유지할 수 있습니다.

이를 활용할 수있는 특히 중요한 방법이 있습니다. quine은 긴 데이터 문자열을 사용하여 작동하며 기본적으로 quine의 모든 출력은 다양한 방식으로 데이터 문자열을 형식화하여 생성됩니다. 프로그램에 여러 조각이 있다는 사실에도 불구하고 데이터 문자열은 하나뿐입니다. 따라서 동일한 데이터 문자열을 사용하여 프로그램의 다른 부분을 인쇄 할 수 있어야합니다. "정크 데이터는 중요하지 않습니다"트릭을 통해 매우 간단한 방법으로이 작업을 수행 할 수 있습니다. ASCII 코드에 값을 더하거나 빼서 프로그램을 구성하는 문자를 데이터 문자열에 저장합니다. 특히, 프로그램의 시작을 구성하는 문자는 ASCII 코드 + 4로 저장되며, 문자는 ASCII 코드-4로 거의 241 배 반복되는 섹션을 구성합니다.오프셋이있는 데이터 문자열의 모든 문자; 예를 들어 모든 문자 코드에 4를 추가하여 인쇄하면 반복되는 섹션이 한 번 반복되고 전후에 주석이 추가됩니다. (이러한 설명은 프로그램의 다른 섹션으로, 잘못된 오프셋이 추가 되었기 때문에 유효한 Brain-Flak 명령을 형성하지 않도록 문자 코드가 이동 된 것입니다. 문제의 부분을 위반하지 않는 Flak 명령 ; 오프셋 선택은 이것을 보장하도록 설계되었습니다.)

이 주석 트릭으로 인해 실제로 a) 소스와 동일한 방식으로 인코딩 된 b) 모든 코드에 지정된 오프셋이 추가 된 문자 코드와 같은 두 가지 방식으로 형식화 된 데이터 문자열 만 출력 할 수 있어야합니다. 그것은 추가 길이를 완전히 가치있게 만드는 거대한 단순화입니다.

프로그램 구조

이 프로그램은 인트로, 데이터 문자열, 데이터 문자열 포맷터 및 아웃트로의 네 부분으로 구성됩니다. 인트로 및 아웃트로는 기본적으로 데이터 문자열 및 해당 포맷터를 루프에서 실행하여 매번 적절한 형식 (예 : 인코딩 또는 오프셋 여부 및 사용할 오프셋)을 지정합니다. 데이터 문자열은 데이터 일 뿐이며, 데이터 문자열을 구성하는 문자가 데이터 문자열에 문자 그대로 지정되지 않은 퀴인의 유일한 부분입니다 (자체보다 길어야하기 때문에 불가능할 것임). 따라서 자체 재생이 특히 쉬운 방식으로 작성되었습니다. 데이터 스트링 포맷터는 거의 동일한 부분으로 구성되며, 각각은 데이터 스트링에서 241 중에서 특정 데이텀을 포맷한다.

프로그램의 각 부분은 다음과 같이 데이터 문자열 및 해당 포맷터를 통해 생성 될 수 있습니다.

  • 아웃트로를 생성하려면 오프셋 +8로 데이터 문자열의 형식을 지정하십시오.
  • 데이터 문자열 포맷터를 생성하려면 오프셋 +4, 241 배로 데이터 문자열을 포맷하십시오.
  • 데이터 문자열을 생성하려면 데이터 문자열을 소스 형식으로 인코딩하여 형식화하십시오.
  • 인트로를 만들려면 오프셋 -4로 데이터 문자열의 형식을 지정하십시오

우리가해야 할 일은 프로그램의이 부분들이 어떻게 작동하는지 보는 것입니다.

데이터 문자열

(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …

Mini-Flak 코드에서 인코딩을 되돌릴 수 있어야하기 때문에 데이터 문자열에 대한 간단한 인코딩이 필요합니다. 이보다 훨씬 간단 해 질 수는 없습니다!

이 퀴네 (주석 트릭 제외)의 핵심 아이디어는 기본적으로 많은 양의 데이터를 저장할 수있는 위치가 하나뿐이라는 것입니다. (이것은 일반적으로 세 번째 스택 으로 알려져 있습니다.Mini-Flak에는 두 번째 스택이 없지만 "작업 스택"은 Mini-Flak 컨텍스트에서 더 나은 이름 일 것입니다. 데이터를 저장할 수있는 다른 가능성은 기본 / 첫 번째 스택 (작동하지 않음)입니다. 출력이 이동해야하는 곳이기 때문에 원격으로 효율적인 방식으로 스토리지를지나 출력을 이동할 수 없으며 단일 스택 요소에서 큰 숫자로 인코딩됩니다 (이 문제에는 적합하지 않습니다. 그것으로부터 데이터를 추출); 그것들을 제거하면 작업 스택이 유일한 남은 위치입니다.

이 스택에 데이터를 "저장"하기 위해 언밸런스 드 명령 (이 경우 (…)명령의 전반)을 사용합니다.이 명령은 나중에 데이터 문자열 포맷터 내에서 균형을 유지합니다. 포맷터 내에서 이러한 명령 중 하나를 닫을 때마다 데이터 문자열에서 가져온 데이텀의 합계와 포맷터 내의 해당 중첩 수준에있는 모든 명령의 반환 값이 푸시됩니다. 우리는 후자가 0에 추가되도록 할 수 있으므로 포맷터는 단순히 데이터 문자열에서 가져온 단일 값을 볼 수 있습니다.

형식은 매우 간단합니다. (다음에 n 개의 복사본이옵니다 (). 여기서 n 은 저장하려는 숫자입니다. (이것은 음수가 아닌 숫자 만 저장할 수 있으며 데이터 문자열의 마지막 요소는 양수 여야합니다.)

데이터 문자열에 대한 약간 직관적이지 않은 점은 순서에 있습니다. 데이터 문자열의 "시작"은 프로그램의 시작에 가장 가까운 끝입니다. 즉 가장 바깥 쪽 중첩 수준입니다. 이 부분은 포맷터가 가장 안쪽에서 가장 바깥쪽으로 중첩 될 때 마지막으로 포맷됩니다. 그러나 마지막 포맷에도 불구하고, 그것은 도착 인쇄 스택으로 푸시 값이 첫 번째 미니 플랙 인터프리터에서 마지막으로 인쇄되기 때문에, 첫 번째. 동일한 원칙이 프로그램 전체에 적용됩니다. 먼저 아웃트로, 데이터 스트링 포맷터, 데이터 스트링, 인트로, 즉 프로그램에 저장된 순서의 역순으로 포맷해야합니다.

데이터 문자열 포맷터

)[({})(
    ([({})]({}{}))
    {
        ((()[()]))
    }{}
    {
        {
            ({}(((({}())[()])))[{}()])
        }{}
        (({}))
        ((()[()]))
    }{}
)]{}

데이터 스트링 포맷터는 각각 동일한 코드 (하나의 섹션은 약간 다른 주석을 가짐)를 갖는 241 개의 섹션으로 구성되며, 각 섹션은 데이터 스트링의 하나의 특정 문자를 포맷합니다. (우리는 여기서 루프를 사용할 수 없었습니다 : unbalanced )를 일치시켜 데이터 문자열을 읽으려면 언밸런스가 필요하며 (, 그중 하나를 안에 넣을 수 없습니다{…} 합니다. 그리고 유일한 루프 형태 인 루프 . 대신 " 포맷터를 풀고 "소개 / 아웃트로 포맷터의 오프셋이 241 번인 데이터 문자열을 출력하도록합니다.)

)[({})( … )]{}

포맷터 요소의 가장 바깥 부분은 데이터 문자열의 한 요소를 읽습니다. 데이터 문자열 인코딩의 단순성으로 인해 읽기가 약간 복잡해집니다. (…)데이터 문자열에서 일치하지 않는 항목을 닫고 시작 하여 데이터 문자열에서 […]읽은 데이텀 ({})과 나머지 프로그램의 반환 값을 두 가지 값으로 무시합니다 ( ). 나머지 포맷터 요소의 반환 값을로 (…)복사하고를 사용하여 부정 버전에 복사본을 추가합니다 {}. 최종 결과는 데이터 스트링 요소와 포매터 요소의 리턴 값이 데이텀에서 데이텀에서 리턴 값에 리턴 값을 더한 값 또는 0을 뺀 것입니다. 이것은 다음 데이터 문자열 요소가 올바른 값을 생성하도록하는 데 필요합니다.

([({})]({}{}))

포맷터는 최상위 스택 요소를 사용하여 현재 모드를 확인합니다 (0 = 데이터 문자열 형식의 형식, 다른 값 = 출력 할 오프셋). 그러나 데이터 문자열을 읽은 후에는 데이텀이 스택의 형식 위에 있으며 다른 방식으로 원합니다. 이 코드는 복용 뇌 플랙 스왑 코드 짧은 변이체이다 상기 (B)를(B) 상기  +의  B ; 추가의 부작용 때문에 그것이 짧을뿐만 아니라, 그것은 유용한 (이 특정한 경우에)이기도 B를A가 경우에 문제가없는 , b는 0이며, 경우 b는 0이 아니라, 의견에 대한 오프셋의 계산을한다.

{
    ((()[()]))
}{}
{
    …
    ((()[()]))
}{}

Brain-Flak은 하나의 제어 흐름 구조 만 가지고 있으므로 while루프 이외의 것을 원하면 약간의 작업이 필요합니다. 이것은 "네거티브"구조입니다. 스택 위에 0이 있으면 제거하고, 그렇지 않으면 스택 위에 0을 배치합니다. 스택의 상단에 0이없는 한 스택에 1-1을 두 번 밀어 넣으십시오. 완료되면 상단 스택 요소를 팝하십시오.

여기 보이는 것처럼 부정 구조 안에 코드를 넣을 수 있습니다. 스택 맨 위가 0이 아닌 경우에만 코드가 실행됩니다. 따라서 두 개의 부정 구조가있는 경우 상위 두 스택 요소가 아니라고 가정합니다. 모두 0 서로를 취소하지만 첫 번째 구조 내부의 모든 코드는 최상위 스택 요소가 0이 아닌 경우에만 실행됩니다. 두 번째 구조는 상단 스택 요소가 0 인 경우에만 실행됩니다. 다시 말해, 이것은 if-then-else 문과 같습니다.

형식이 0이 아닌 경우 실행되는 "then"절에서 실제로 수행 할 작업이 없습니다. 우리가 원하는 것은 데이터 + 오프셋을 메인 스택으로 푸시하는 것입니다 (프로그램 끝에서 출력 할 수 있도록). 그러나 이미 있습니다. 따라서 데이터 문자열 요소를 소스 형식으로 인코딩하는 경우 만 처리하면됩니다.

{
    ({}(((({}())[()])))[{}()])
}{}
(({}))

그 방법은 다음과 같습니다. 이 {({}( … )[{}()])}{}구조는 특정 반복 횟수를 가진 루프로 친숙해야합니다 (루프 카운터를 작업 스택으로 이동하고 유지하여 작동합니다. 작업 스택에 대한 액세스가 묶여 있기 때문에 다른 코드로부터 안전합니다) 프로그램의 중첩 수준). 루프의 본문은 ((({}())[()]))입니다. 그러면 최상위 스택 요소의 사본이 3 개 만들어지고 가장 낮은 값에 1이 추가됩니다. 즉, 스택 상단의 40을 41 이상의 40 이상, 또는 ASCII (로 본 40 으로 변환합니다 ((). 할 것이다 반복적으로 실행 ((()(()()(()()()따라서 등등, 그리고 것이 우리의 데이터 문자열을 생성하는 간단한 방법은 (a가 있다고 가정 (스택의 상단에 이미).

루프가 끝나면 (({}))스택의 상단을 복제하여 (지금이 ((()…아니라 시작합니다 (()…. (데이터 문자열 포맷터의 다음 사본에서 행간 을 사용하여 다음 문자를 형식화합니다. (()(()…그런 다음 데이터 문자열에서 (()()(()…분리 (를 생성 합니다).

%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'

데이터 문자열 포맷터에는 마지막으로 중요한 부분이 있습니다. 자, 이것은 대부분 아웃트로가 4 개의 코드 포인트를 아래쪽으로 이동 한 것입니다. 그러나 끝에있는 아포스트로피가 제대로 보이지 않을 수 있습니다. '(codepoint 39)는 +Brain-Flak 명령이 아닌 (codepoint 43) 으로 이동 하므로 다른 목적으로 사용되었다고 추측했을 수 있습니다.

여기에있는 이유는 데이터 문자열 포맷터가 (이미 스택에 있을 것으로 예상하기 때문입니다 (어디서나 리터럴 40을 포함하지 않음). 그만큼'실제로 데이터 블록 포맷터를 구성하기 위해 반복되는 블록의 시작 부분에 있습니다. 따라서 데이터 문자열 포맷터의 문자가 스택으로 푸시 된 후 (코드는 데이터 문자열 인쇄로 이동하려고합니다. outro는 스택 맨 위에있는 39를 40으로 조정하여 포맷터 (이번 실행중인 포맷터 자체가 소스에서 표시되지 않음)를 사용하도록 준비합니다. 따라서 포맷터의 "거의 241"사본이 있습니다. 첫 번째 사본에 첫 번째 문자가 없습니다. 그리고 아포스트로피라는 문자는 프로그램의 어딘가에 Mini-Flak 코드와 일치하지 않는 데이터 문자열에서 세 문자 중 하나입니다. 상수를 제공하는 방법으로 순수하게 존재합니다.

소개와 아웃트로

(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                …
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

인트로와 아웃트로는 개념적으로 프로그램의 동일한 부분입니다. 우리가 구별하는 유일한 이유는 데이터 문자열과 그 포맷터가 출력되기 전에 아웃트로가 출력되어야하고 (그 후에 출력되도록) 인트로가 출력되고 나서 출력이 필요하기 때문입니다.

(((()()()()){}))

스택에 8 장의 사본 두 개를 놓는 것으로 시작합니다. 이것은 첫 번째 반복에 대한 오프셋입니다. 두 번째 사본은 메인 루프가 오프셋 위에 스택 위에 정크 요소가있을 것으로 예상하기 때문에 메인 루프의 존재 여부를 결정하는 테스트에서 제외되어 정크 요소를 배치해야합니다. 실제로 원하는 요소를 버리지 않습니다. 사본은 그렇게하는 가장 현명한 (따라서 가장 빠른 출력) 방법입니다.

이 숫자보다 길지 않은 숫자 8의 다른 표현이 있습니다. 그러나 가장 빠른 코드를 사용하려면 이것이 최선의 선택입니다. 우선, 사용하는 것은 ()()()()빠르고,보다 말, (()()){}때문에 긴 두있는 8 자에도 불구하고, 전자는,주기 빠른이기 때문에 (…)하지만, 2 사이클로 계산됩니다 ()하나있다. 한 사이클을 저장하는 것은에 대한 훨씬 더 큰 배려에 비해 무시할 수 있지만 : (그리고 )많이보다 낮은 코드 포인트가 {하고 }, 그래서 그들에 대한 데이터 조각을 생성하는 훨씬 더 빨리 될 것입니다 (데이터 조각은 코드에서 공간을 적게 걸릴 것이다, 너무).

{{} … }{}{}

메인 루프. 반복 횟수를 계산하지 않습니다 ( while루프가 아니라 for루프이며 테스트를 사용하여 나눕니다). 종료되면 상위 2 개의 스택 요소를 버립니다. 맨 위의 요소는 무해한 0이지만 아래의 요소는 "다음 반복에 사용할 형식"입니다 (음수 오프셋 임)는 음수이며, Mini 일 때 스택에 음수가있는 경우 -Flak 프로그램이 종료되고 인터프리터가 출력을 시도하는 동안 충돌이 발생합니다.

이 루프는 명시 적 테스트를 사용하여 나눠지기 때문에 해당 테스트 결과는 스택에 남게되므로이를 첫 번째로 폐기합니다 (값은 유용하지 않음).

(({})[(()()()())])

이 코드는  스택 요소 f 위에 4와 f -4를 푸시 하면서 해당 요소를 그대로 둡니다. 우리는 다음 반복에 대한 형식을 미리 계산하고 (상수 4가 편리한 동안) 프로그램의 다음 몇 부분에 대해 올바른 순서로 스택을 가져옵니다. 우리는 f 를 형식으로 사용합니다. 이 반복과 그 이전에 4가 필요합니다.

(({})( … )[{}])

이렇게하면  작업 스택에 f -4 의 사본이 저장 되어 다음 반복에 사용할 수 있습니다. ( f 의 값 은 여전히 ​​그 시점에 존재하지만 스택의 어색한 위치에있을 것입니다. 심지어 올바른 위치로 조작 할 수 있다고해도 4를 빼는주기를 소비해야합니다. 뺄셈을하기 위해 코드를 인쇄하는 과정을주기 때문입니다.

{{}{}((()[()]))}{}

오프셋이 4인지 확인하기위한 테스트입니다 (예 : f  -4는 0). 그렇다면 데이터 문자열 포맷터를 인쇄하는 것이므로이 오프셋에서 한 번이 아니라 데이터 문자열과 해당 포맷터를 241 번 실행해야합니다. 코드는 매우 간단합니다. f  -4가 0이 아닌 경우 f  -4와 4 자체를 한 쌍의 0으로 바꾸십시오 . 두 경우 모두 상단 스택 요소를 팝합니다. 스택에 f 이상의 숫자 가 4 (이 반복을 241 회 인쇄하려는 경우) 또는 0 (한 번만 인쇄하려는 경우)이 있습니다.

(
    ((((((({})){}){}{})){}{}){}){}
    ()
)

이것은 흥미로운 종류의 Brain-Flak / Mini-Flak 상수입니다. 여기서 긴 줄은 숫자 60을 나타냅니다. ()Brain-Flak 상수의 모든 곳에있는 의 부족으로 혼란 스러울 수 있습니다 . 이것은 일반 숫자가 아니라 숫자를 복제 작업으로 해석하는 교회 숫자입니다. 예를 들어, 여기에서 볼 수있는 60의 교회 번호는 60 개의 입력을 만들어 단일 값으로 결합합니다. Brain-Flak에서, 우리가 결합 할 수있는 유일한 것은 또한 정규수입니다. 그래서 우리는 스택의 상단에 60 개의 복사본을 더해서 스택의 상단에 60을 곱합니다.

참고로, Underload 구문에서 교회 숫자를 생성 하는 Underload numeric finder를 사용하여 Mini-Flak에서도 적절한 숫자를 찾을 수 있습니다. 언더로드 숫자 (0이 아닌 숫자)는 "중복 스택 요소" :및 "상단 두 스택 요소 결합 "연산을 사용합니다 *. 이 두 가지 작업은 Brain-Flak에 존재하므로 , 로 번역 :하고 앞에 추가하고 균형을 잡기 시작할 때 충분히 추가 하십시오 (주 스택과 작업 스택의 이상한 혼합을 사용하지만 작동합니다).)*{}{}(

이 특정 코드 조각은 60 x  + 1 의 식을 생성하기 위해 증분과 함께 교회 숫자 60 (효과적으로 "60으로 곱하기"스 니펫)을 사용합니다 . 따라서 이전 단계에서 4를 가졌다면 이는 241의 값을 가지거나 0을 가졌다면 1의 값을 얻습니다. 즉, 필요한 반복 횟수를 정확하게 계산합니다.

241의 선택은 우연이 아닙니다. a) 프로그램이 끝나는 길이와 b) 라운드 수의 4 배를 초과하는 값으로 선택되었습니다. 이 경우 60의 둥근 숫자는 복사 할 요소가 더 유연하기 때문에 교회 숫자로 더 짧은 표현을하는 경향이 있습니다. 이 프로그램에는 나중에 길이를 정확히 241까지 가져 오는 패딩이 포함되어 있습니다.

{
    ({}(
        …
    )[{}()])
}{}

이것은 이전에 본 것과 같은 for 루프이며, 메인 스택의 최상위 (소비; 루프 카운터 자체는 작업 스택에 저장되지만 가시성은 이는 프로그램의 중첩 수준에 연결되어 있으므로 for 루프 자체와 상호 작용하는 것은 불가능합니다. 이것은 실제로 데이터 문자열과 해당 포맷터를 1 또는 241 번 실행하며, 이제 메인 스택에서 제어 흐름 계산에 사용했던 모든 값을 표시 했으므로 맨 위에 사용할 수있는 형식이 있습니다. 사용할 포맷터

(␀␀!S␠su! … many more comment characters … oq␝qoqoq)

이 의견은 전혀 관심이없는 것은 아닙니다. 우선, 몇 가지 Brain-Flak 명령이 있습니다. )끝에 자연스럽게 프로그램 작업의 다양한 세그먼트 사이의 전환은, 그렇게하는 방식의 부작용으로 발생하는 (코멘트 내부를 넣고, 시작시 수동으로 균형 (및 주석 내부의 길이에도 불구하고 추가되었습니다 ()명령은 여전히있다 ()가 않습니다 모두 반환 데이터 문자열의 값과 포맷, for 루프 완전히 무시 무언가)에 1을 추가하므로, 명령.

특히 주석 시작 부분의 NUL 문자는 어떤 것도 오프셋되지 않습니다 (+8과 -4의 차이 (로도 NUL로 전환하기에는 충분하지 않습니다 ). 그것들은 239 요소 데이터 문자열을 최대 241 요소까지 가져 오는 순수한 패딩입니다 (쉽게 지불하십시오 : 필요한 반복 횟수를 계산할 때 1 대 241 대신 1 대 239를 생성하는 데 2 ​​바이트 이상이 소요됩니다) ). NUL은 코드 포인트가 가장 낮기 때문에 패딩 문자로 사용되었습니다 (데이터 문자열의 소스 코드가 더 짧아서 출력 속도가 더 빠름).

{}({}())

맨 위 스택 요소 (사용중인 형식)를 삭제하고 다음에 1을 추가하십시오 (마지막으로 포맷 한 프로그램 섹션의 마지막 문자, 즉 인쇄 할 첫 문자). 이전 형식은 더 이상 필요하지 않습니다 (새로운 형식은 작업 스택에 숨어 있습니다). 증분은 대부분의 경우 무해 '하며 데이터 문자열 포맷터의 소스 표현의 한쪽 끝을 ((다음에 포맷터를 실행할 때 스택에서 데이터 문자열 자체를 포맷하기 위해 필요한 스택으로) 변경합니다. 우리는 시작하는 요소 포맷터 각 데이터 문자열을 강제하기 때문에, 아웃트로 또는 소개에 그런 변화를 필요 (는 다소 복잡 할 것 (우리가를 닫 필요할 것으로 (하고 나중에 그 효과를 실행 취소) 우리 는 포맷터의 사본 (거의 241 개에 불과하기 때문에 어딘가에 여분의 파일을 생성해야합니다 (모두 241이 아닌 무해한 문자가없는 것이 가장 좋습니다 ').

(({})(()()()()){})

마지막으로 루프 종료 테스트. 메인 스택의 현재 최상위는 다음 반복에 필요한 형식입니다 (작업 스택에서 방금 돌아옴). 이것은 그것을 복사하고 사본에 8을 추가합니다. 결과 값은 다음에 루프를 돌 때 폐기됩니다. 그러나 인트로를 방금 인쇄 한 경우 오프셋은 -4이므로 "다음 반복"의 오프셋은 -8이됩니다. -8 + 8은 0이므로 루프는 나중에 반복을 계속하지 않고 종료됩니다.


16

128,673,515 회

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

설명

Miniflak quine이 느려지는 이유는 Miniflak의 임의 액세스가 없기 때문입니다. 이 문제를 해결하기 위해 숫자를 가져 와서 데이텀을 반환하는 코드 블록을 만듭니다. 각 데이텀은 이전과 같은 단일 문자를 나타내며 기본 코드는이 블록을 한 번에 하나씩 간단히 쿼리합니다. 이것은 본질적으로 랜덤 액세스 메모리의 블록으로 작동합니다.


이 코드 블록에는 두 가지 요구 사항이 있습니다.

  • 숫자를 가져와 해당 문자의 문자 코드 만 출력해야합니다.

  • Brain-Flak에서 조회 테이블을 비트 단위로 쉽게 재현 할 수 있어야합니다.

이 블록을 구성하기 위해 실제로 Miniflak가 Turing complete라는 내 증거의 방법을 재사용했습니다. 각 데이텀마다 다음과 같은 코드 블록이 있습니다.

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

스택 맨 위의 숫자에서 1을 빼고 0이되면 %s그 아래에있는 데이텀을 밉니다. 스택에서 n으로 시작하면 각 조각의 크기가 하나씩 감소하므로 n 번째 데이텀을 다시 얻게됩니다.

이것은 훌륭하고 모듈 식이므로 프로그램으로 쉽게 작성할 수 있습니다.


다음으로이 메모리를 실제로 소스로 변환하는 머신을 설정해야합니다. 이것은 다음과 같은 세 부분으로 구성됩니다.

(([()]())())
{({}[(
  -Look up table-
 )]{})
 1. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}(([{}]))(()()()()()))]{})}{}

 2. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
      (({}[(
      ({}[()(((((()()()()()){}){}){}))]{}){({}[()(({}()))]{}){({}[()(({}((((()()()){}){}){}()){}))]{}){({}[()(({}()()))]{}){({}[()(({}(((()()()()())){}{}){}))]{}){([(({}{}()))]{})}}}}}{}
      (({}({}))[({}[{}])])
     )]{}({})[()]))
      ({[()]([({}({}[({})]))]{})}{}()()()()()[(({}({})))]{})
    )]{})}{}

 3. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
     (({}(({}({}))[({}[{}])][(
     ({}[()(
      ([()](((()()[(((((((()()()){})())){}{}){}){})]((((()()()()())){}{}){})([{}]([()()](({})(([{}](()()([()()](((((({}){}){}())){}){}{}))))))))))))
     )]{})
     {({}[()(((({})())[()]))]{})}{}
     (([(((((()()()()){}){}()))){}{}([({})]((({})){}{}))]()()([()()]({}(({})([()]([({}())](({})([({}[()])]()(({})(([()](([({}()())]()({}([()](([((((((()()()())()){}){}){}()){})]({}()(([(((((({})){}){}())){}{})]({}([((((({}())){}){}){}()){}()](([()()])(()()({}(((((({}())())){}{}){}){}([((((({}))){}()){}){}]([((({}[()])){}{}){}]([()()](((((({}())){}{}){}){})(([{}](()()([()()](()()(((((()()()()()){}){}){}()){}()(([((((((()()()())){}){}())){}{})]({}([((((({})()){}){}){}()){}()](([()()])(()()({}(((((({}){}){}())){}){}{}(({})))))))))))))))))))))))))))))))))))))))))))))))
     )]{})[()]))({()()()([({})]{})}{}())
    )]{})}{}

   ({}[()])
}{}{}{}
(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

기계는 1부터 시작하여 3으로 끝나는 4 개의 부품으로 구성되어 있습니다. 위의 코드에서 레이블을 지정했습니다. 각 섹션은 인코딩에 사용하는 것과 동일한 조회 테이블 형식을 사용합니다. 전체 프로그램이 루프에 포함되어 있기 때문에 루프를 실행할 때마다 모든 섹션을 실행하고 싶지 않기 때문에 동일한 RA 구조를 설정하고 매번 원하는 섹션을 쿼리합니다.

1

섹션 1은 간단한 설정 섹션입니다.

이 프로그램은 첫 번째 쿼리 섹션 1과 데이텀 0을 알려줍니다. 데이텀 0은 존재하지 않으므로 해당 값을 반환하는 대신 각 데이텀에 대해 쿼리를 한 번만 감소시킵니다. 결과를 사용하여 데이터 수를 결정할 수 있기 때문에 유용합니다. 이는 향후 섹션에서 중요해질 것입니다. 섹션 1은 결과를 무시하고 섹션 2와 마지막 데이텀을 쿼리하여 데이터 수를 기록합니다. 유일한 문제는 섹션 2를 직접 쿼리 할 수 ​​없다는 것입니다. 다른 감소가 남아 있기 때문에 존재하지 않는 섹션 5를 쿼리해야합니다. 실제로는 다른 섹션 내의 섹션을 쿼리 할 때마다 발생합니다. 내 설명에서 이것을 무시할 것입니다. 코드를보고 있다면 5는 섹션을 다시 이동하고 4는 동일한 섹션을 다시 실행한다는 것을 기억하십시오.

2

섹션 2는 데이터 블록 다음에 코드를 구성하는 문자로 데이터를 디코딩합니다. 스택이 다음과 같이 나타날 것으로 기대할 때마다 :

Previous query
Result of query
Number of data
Junk we shouldn't touch...

가능한 모든 결과 (1에서 6까지의 숫자)를 6 개의 유효한 Miniflak 문자 ( (){}[]) 중 하나에 매핑 하고 "터치해서는 안되는 청크 "를 사용하여 데이터 수 아래에 배치합니다. 이것은 우리에게 다음과 같은 스택을 얻습니다.

Previous query
Number of data
Junk we shouldn't touch...

여기에서 다음 데이텀을 쿼리하거나 쿼리 한 경우 모두 섹션 3으로 이동해야합니다. 이전 쿼리는 실제로 전송 된 정확한 쿼리가 아니라 쿼리에서 블록의 데이터 수를 뺀 것입니다. 각 데이텀이 쿼리를 하나씩 감소시키기 때문에 쿼리가 엉망이되기 때문입니다. 다음 쿼리를 생성하기 위해 데이터 수의 복사본을 추가하고 하나를 뺍니다. 이제 스택은 다음과 같습니다.

Next query
Number of data
Junk we shouldn't touch...

다음 쿼리가 0이면 섹션 3에 필요한 모든 메모리를 읽었으므로 쿼리에 데이터 수를 다시 추가하고 스택 맨 위에 4를 쳐서 섹션 3으로 이동합니다. 다음 쿼리가 0이 아닌 경우 섹션 2를 다시 실행하려면 스택에 5를 넣으십시오.

섹션 3은 섹션 3과 마찬가지로 RAM을 쿼리하여 데이터 블록을 만듭니다.

간결성을 위해 섹션 3의 작동 방식에 대한 자세한 내용은 생략합니다. 각 데이텀을 하나의 문자로 변환하는 대신 RAM에서 해당 항목을 나타내는 긴 코드 덩어리로 변환한다는 점을 제외하면 섹션 2와 거의 동일합니다. 섹션 3이 완료되면 프로그램이 루프를 종료하도록 지시합니다.


루프가 실행 된 후 프로그램은 quine의 첫 번째 비트 만 푸시하면 ([()]())(()()()()){({}[(됩니다. 표준 Kolmogorov 복잡성 기술을 구현하는 다음 코드 로이 작업을 수행합니다.

(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

나는 이것이 분명했으면 좋겠다. 혼란스러운 점이 있으면 의견을 말하십시오.


얼마나 오래 걸립니까? 그것은 TIO에 있습니다.
Pavel

@Pavel TIO에서는 엄청나게 느리기 때문에 TIO에서 실행하지 않습니다 .TIO가 사용하는 것과 동일한 인터프리터 ( 루비 하나 )를 사용합니다. 내가 액세스 할 수있는 오래된 랙 서버에서 실행하는 데 약 20 분이 걸립니다. Crain-Flak에서는 약 15 분이 걸리지 만 Crain-Flak에는 디버그 플래그가 없으므로 Ruby 인터프리터에서 실행하지 않으면 점수를 매길 수 없습니다.
밀 마법사

@Pavel 나는 그것을 다시 실행하고 시간을 정했다. 그것은했다 30m45.284s루비 인터프리터를 사용하여 다소 낮은 엔드 서버 (A 평균 현대 데스크톱의 약에 해당)에 완료.
밀 마법사
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.