유효한 Brainf ** k 프로그램 열거


41

Golunar / Unary 는 모든 유효한 Brainfuck 프로그램 을 인코딩하는 방법 이지만 대부분의 자연수가 유효한 프로그램과 일치하지 않기 때문에 열거 형이 아닙니다.

이 과제의 목적 상, 두 개의 무한 테이프를 사용하고 주석이없는 것으로 가정합니다. 즉, Brainfuck 프로그램은 문자로만 구성 <>+-.,[]되고 모든 왼쪽 및 오른쪽 대괄호가 일치 하는 경우에만 유효합니다 .

예를 들어, 비어있는 프로그램은 ,[+][-]., [>+<[--].]+[+[+][+[+]+]+]+.유효 브레인 퍽 프로그램 동안은 ][, 그리고 a[]이 아니다.

태스크

유효한 Brainfuck 프로그램을 입력으로 허용 하고 다음 제한 조건 으로 자연수 ( 1 , 2 , 3 ,…)를 리턴하는 프로그램 또는 함수를 작성하십시오 .

  • 생성 된 출력은 모든 유효한 Brainfuck 프로그램마다 달라야합니다.

  • 모든 자연수 n 에 대해 입력으로 제공 될 때 출력 n을 생성하는 유효한 Brainfuck 프로그램이 있어야합니다 .

추가 규칙

  • 100 바이트 이하의 Brainfuck 프로그램이 제공되면 프로그램 또는 기능은 1 분 이내에 완료되어야합니다.

    이것은 입력과 일치 할 때까지 모든 유효한 Brainfuck 프로그램을 반복 할 수 없음을 의미합니다.

  • 표준 규칙이 적용됩니다.


3
나는 그것을 8 진수로 인코딩하려고 생각했지만 일치하는 대괄호는 이것을 까다롭게 만듭니다.
DankMemes

빈 프로그램이 유효한 Brainfuck 프로그램입니까? 자연 정수에도 매핑해야합니까?
orlp

9
왜 가까운 투표? 이것은 흥미로운 질문 이며 문제는 훌륭한 골프를위한 크기와 다양성을 갖는 것입니다.
trichoplax

1
@orlp 예, 빈 프로그램은 위의 정의를 만족합니다
Dennis

3
아직도 Brainfuck로 작성된 답변을보기를 기다리는 중 ...
Michael Hampton

답변:


16

파이썬 3 443 158 155 154 134 131 128 124 117 116 115 바이트

c=d=C=D=0
for e in input():v='[<>,.-+]'.find(e);d=d*8+v;c+=c<0<6<v;c-=d>1>v;C,D=(c,C+1,d,D)[v>6::2]
print(-~D*8**C)

Sp3000 및 Mitch Schwartz 덕분에 몇 바이트 : D

작동 원리 :

이것은 모든 유효한 BF 프로그램을 [일대일 비율로로 시작하지 않는 가능한 모든 유효하거나 유효하지 않은 BF 프로그램에 매핑합니다 . 그 후, 새로운 프로그램은 단순히 8 진수로 변환됩니다.

매핑 공식은 다음과 같습니다.

  1. BF 프로그램을 세 부분으로 분리하십시오. 첫 번째 부분은 [문자 로만 구성된 가장 큰 접두사 입니다. 세 번째 부분은 ]문자 로만 구성된 가장 큰 접미사 입니다. 두 번째 부분은 중간입니다.
  2. 첫 번째 부분을 폐기하십시오. 이것들은 나중에 다시 계산할 수 있습니다.
  3. 두 번째 부분의 브래킷 ]과 일치하는 세 번째 부분의 모든 브래킷을 제거하십시오 [. 나중에 다시 계산할 수도 있습니다.
  4. 두 번째 부분과 세 번째 부분을 함께 연결하십시오.

이 설명을 이해하지 못하는 경우 여기 에서 시작하는 채팅에서 자세한 설명을 찾을 수 있습니다 .

참고로 처음 20 개 프로그램은 다음과 같습니다.

1 : 
2 : <
3 : >
4 : ,
5 : .
6 : -
7 : +
8 : []
9 : <[]
10 : <<
11 : <>
12 : <,
13 : <.
14 : <-
15 : <+
16 : [<]
17 : >[]
18 : ><
19 : >>
20 : >,

처음 1000 개의 프로그램 은 다음과 같습니다 . http://pastebin.com/qykBWhmD
다음은 프로그램을 생성하는 데 사용한 프로그램입니다. http://ideone.com/e8oTVl

여기 있습니다 Hello, World!:

>>> ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
457711481836430915510337664562435564418569135809989841510260388418118348571803953323858180392373

작은 퀴즈 : 프로그램을 0에 매핑 할 수 없습니다 .
Dennis

@Dennis 빈 프로그램은 프로그램으로 간주됩니까?
Beta Decay


@Dennis 나는 골프 때 그것을 고칠 것입니다.
TheNumberOne

3
이것은 독창적입니다
자랑스런 Haskeller

13

파이썬 2, 157 바이트

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x

여전히 꽤 골프 타는 것처럼 보이지만 지금은 이것을 게시하고 있습니다. 약간의 캐싱으로 재귀를 사용합니다. 성가신 D.get것은 캐싱을 단락시키지 않으므로 9 바이트를 절약 할 수 없습니다 ...

매핑의 우선 순위는 우선 순위를 정한 다음 순서보다 사전 사전 순입니다 "][><.-,+"(아래 출력 예 참조). 주요 아이디어는 접두사를 비교하는 것입니다.

변수 는 현재 접두사에 대해 여전히 열려 o있는 [괄호 수를 추적 하며 변수 d는 다음을 나타내는 세 가지 값 중 하나를 사용합니다.

  • d = 1: 현재 접두어가 사전 적으로 사전 s입니다. 이 접두사와 길이를 가진 모든 프로그램을 추가하십시오 <= s.
  • d = -1: 현재 접두어가 사전 적으로 이후 s입니다. 이 접두사와 길이를 가진 모든 프로그램을 추가하십시오 < s.
  • d = 0: 현재 접두사는 접두사 s이므로 d나중에 1 또는 -1로 변경할 수 있습니다 .

예를 들어, 우리는이 있다면 s = "[-]"우리의 현재의 접두사입니다 p = "+"때문에, p보다 이후 s전적으로 우리가 시작하는 프로그램 추가 만 알고 p있는보다 엄격하게 짧다 s.

보다 자세한 예제를 제공하기 위해 입력 프로그램이 있다고 가정하십시오 s = "-[]". 첫 번째 재귀 확장은 다음을 수행합니다.

  (o == 0)               # Adds a program shorter than s if it's valid
                         # For the first expansion, this is 1 for the empty program
+ f(s[1:], o=-1, d=1)    # ']', o goes down by one due to closing bracket
+ f(s[1:], o=1, d=1)     # '[', o goes up by one due to opening bracket
+ f(s[1:], o=0, d=1)     # '>'
+ f(s[1:], o=0, d=1)     # '<'
+ f(s[1:], o=0, d=1)     # '.', d is set to 1 for this and the previous branches
                         # since they are lexicographically earlier than s's first char
+ f(s[1:], o=0, d=0)     # '-', d is still 0 since this is equal to s's first char
+ f(s[1:], o=0, d=-1)    # ',', d is set to -1 for this and the later branches
                         # since they are lexicographically later than s's first char
+ f(s[1:], o=0, d=-1)    # '+'

우리가 그들에 대해 관심을 모든 변수를 통해 캡처 - 주 우리는 실제로 재귀의 접두사를 사용하지 않는 방법 d, o및 축소 입력 프로그램 s. 위와 같이 반복이 많이 발생하는 것을 알 수 있습니다.이 부분에서 캐싱이 발생하여 시간 제한 내에 100 개 문자 프로그램을 처리 할 수 ​​있습니다.

s비어있는, 우리가 볼 (d>=0 and o==0)일을 반환할지 여부를 결정하는, (이 / 사 전적으로 초기 동일한의 프로그램이 유효하기 때문에이 프로그램을 계산) 또는 0 (이 프로그램을 포함되지 않습니다).

이 접두사가있는 프로그램은보다 s 이기 때문에 유효하지 않기 때문에 o < 0즉시로 리턴하는 모든 situtation은 을 리턴합니다 .0][


처음 20 개의 출력은 다음과 같습니다.

 1
> 2
< 3
. 4
- 5
, 6
+ 7
[] 8
>> 9
>< 10
>. 11
>- 12
>, 13
>+ 14
<> 15
<< 16
<. 17
<- 18
<, 19
<+ 20

@TheNumberOne의 답변과 동일한 Hello World 예제를 사용하십시오.

>>> f("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.")
3465145076881283052460228065290888888678172704871007535700516169748342312215139431629577335423L

4

Python 2, 505 (골프 아님)

나는이 접근 방식을 개발하는 것을 즐겼지만 다른 접근 방식에 비해 경쟁적이지 않기 때문에 골퍼 링을하지 않을 수 있습니다. 다양성을 위해 미적 관심을 끌기 위해 게시하고 있습니다. 재귀와 약간의 수학이 필요합니다.

F={0:1}

def f(n):
    if n not in F:
        F[n]=6*f(n-1) + sum(f(i)*f(n-2-i) for i in range(n-1))

    return F[n]

def h(x):
    if x=='': return 0

    if len(x)==1: return '+-<>,.'.find(x)

    if x[0]!='[':
        return h(x[0]) * f(len(x)-1) + h(x[1:])

    d=i=1
    while d:
        if x[i]==']': d-=1
        elif x[i]=='[': d+=1
        i+=1

    a=i-2
    b=len(x)-i

    return 6*f(a+b+1) + sum(f(i)*f(a+b-i) for i in range(a)) + h(x[1:i-1]) * f(b) + h(x[i:])

def g(x):
    return sum(f(i) for i in range(len(x))) + h(x) + 1

print g(raw_input())

이 함수 f(n)는 length의 유효한 brainfuck 프로그램 수를 계산합니다 n. h(x)지도 길이의 프로그램 n[0..f(n)-1], 그리고 g(x)문제의 전단 사 순위 기능입니다.

비어 있지 않은 프로그램은 문자가 [아닌 6 개의 []문자 중 하나로 시작하거나 시작하지 않는 것이 좋습니다 . 전자의 경우, 일치하는 가능한 위치를 ]반복하고 닫힌 부분과 꼬리에서 재귀를 수행 할 수 있습니다 (꼬리는 ]. 후자의 경우 꼬리를 재귀 할 수 있습니다 (꼬리는 첫 번째 문자를 버리는 것을 의미합니다). 이 추론은 계산 및 계산 순위 모두에 사용될 수 있습니다.

짧은 프로그램은 항상 긴 프로그램보다 순위가 낮으며 괄호 패턴은 2 차 결정 요소입니다. []문자 이외의 문자는 "+-<>"에 따라 정렬됩니다. (임의의)

예를 들어 다음과 n=4같은 경우가 있습니다.

zxxx
[]xx
[x]x
[xx]

여기서는 문자 z가 아닌 []문자를 x나타내고 임의의 문자를 나타내며,이 문자 ]는 초기 문자와 일치해야합니다 [. 프로그램은 그 순서에 따라 순위가 매겨지며 x하위 섹션에서는 재귀 적으로 왼쪽 섹션이 오른쪽 섹션보다 우선합니다. 순위 계산은 혼합 기수 시스템과 유사 f하며 현재 "기수"를 계산하는 데 중요합니다.


4

이 답변은 TheNumberOne 의 답변 , 유효한 Brainf ** k 프로그램 열거 에 대한 공식적인 증거로 , 열거가 올바른 이유를 이해하기가 약간 어려울 수 있습니다. 유효한 프로그램에 포함되지 않은 숫자에 매핑되는 일부 유효하지 않은 프로그램이없는 이유를 이해하는 것은 쉽지 않습니다.

이 답변 전체에서 대문자는 프로그램을 나타내는 데 사용되며 소문자 변수는 함수와 정수에 사용됩니다. ~는 연결 연산자입니다.

제안 1 :

함수 f를 해당 답변에 설명 된 프로그램으로 둡니다. 그런 다음 모든 프로그램 U에 대해 유효한 프로그램 V가 존재하여 f (U) = f (V)

정의 1 :

g (X)를 [프로그램 X에 나타나는 숫자로 하고, h (X) ]를 나타나는 숫자로하십시오 .

정의 2 :

이 함수가되도록 P (x)를 정의하십시오.

P(x) = "" (the empty program) when x <= 0
P(x) = "]" when x = 1
P(x) = "]]" when x = 2
etcetera

정의 3 :

프로그램 X가 주어지면 X1은 [문자 의 가장 큰 접두사 , X2는 중심, X3는 가장 큰 ]문자 접미사로 표시하십시오 .

제안 증명 1 :

g (U) = h (U)이면 U는 유효한 프로그램이며 V = U를 취할 수 있습니다. (사소한 경우).

g (U) <h (U)이면 n = h (U)-g (U) [기호 를 앞에 붙여 V를 만들 수 있습니다 . [접두사의 모든 기호가 제거되므로 분명히 f (V) = f (U) 입니다.

이제 g (U)> h (U)를 고려하십시오. T = U2 ~ U3을 정의하십시오. g (T) <= h (T)이면 n = g (U)-h (U) [기호 를 제거하여 V를 생성 할 수 있습니다 .

따라서 h (T) <g (T)라고 가정 할 수 있습니다. 구축 V = T ~ P (g (T)-h (T)).

진행하려면 세 가지 작은 사실이 필요합니다.

주장 1 : g (U2) = g (T)

U3 [의 정의에 따라 기호가 포함되어 있지 않습니다 . T = U2 ~ U3이므로 [기호는 모두 첫 번째 부분에 있습니다.

주장 2 : h (U3) <g (T)

이것은 h (T) <g (T) 및 h (U3) <h (U3 ~ U2) = h (T)라는 점에서 알 수 있습니다.

주장 3 : h (V3) = g (U2)-h (U2)

h(V3) = h(U3) + g(T) - h(T)                           using the construction of V
h(V3) = h(U3) + g(U2) + g(U3) - h(U2) - h(U3)         apply the definition of T
h(V3) = g(U2) - h(U2) *one term cancels, g(U3)        is always zero, as U3 contains only `]` symbols*

이제 우리는 f (V) = f (U)를 보여줍니다.

f(U) = U2 ~ P(h(U3) - g(U2)) = U2                     claim 2, definition of P

f(V) = U2 ~ P(h(V3) - g(V2))
     = U2 ~ P(h(V3) - g(U2))
     = U2 ~ P(g(U2) - h(U2) - g(U2))                  claim 3
     = U2 ~ P(-h(U2))
     = U2                                             definition P

이것으로 증명이 완료됩니다. QED

독창성도 함께합시다.

제안 2 :

U, V를 두 개의 다른 유효한 프로그램으로하자. 그러면 f (U)! = f (V)

이것은 이전 제안과 비교하여 매우 간단합니다.

U2 = V2라고 가정 해 봅시다. 그러나 U와 V가 다른 수있는 유일한 방법은 각각 n []기호를 U1과 U3 에 추가하거나 제거하는 것 입니다. f는 ]접미사에서 일치하지 않는 기호 의 수를 계산하므로 f의 출력이 변경 됩니다.

따라서 U2! = V2입니다.

분명히 이것은 모순을 초래합니다. U2와 V2는 문자 그대로 f (U)와 f (V)의 출력에 각각 포함되어 있으므로 U2가 U3과 연결된 'edge'를 제외하고는 다를 수 없습니다. 그러나 U2 및 V2의 첫 번째 및 마지막 심볼은 정의에 따라 [또는 ]정의 할 수 없지만 U1, U3, V1, V3에서 각각 또 다시 각각 허용되는 유일한 심볼입니다. 따라서 U2 = V2가됩니다. QED

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