有効なBrainf ** kプログラムを列挙する


41

Golunar / 単項は、すべての有効なコード化するための方法ですBrainfuckのプログラムを、ほとんどの自然数は、有効なプログラムに対応していないので、それは、列挙ではありません。

この課題のために、二重の無限テープとコメントなし、つまり、Brainfuckプログラムが有効であるのは、それが文字のみで構成され<>+-.,[]、左右のすべてのブラケットが一致する場合のみです。

たとえば、空のプログラム、,[+][-].[>+<[--].]および+[+[+][+[+]+]+]+.有効なBrainfuckプログラム、しばらくしている][、とa[]はありません。

仕事

自然数(入力として有効なBrainfuckプログラムを受け入れ、返すプログラムまたは機能書く123次の制約では、、...):

  • 生成される出力は、すべての有効なBrainfuckプログラムで異なる必要があります。

  • すべての自然数nに対して、入力として提供されたときに出力nを生成する有効なBrainfuckプログラムが必要です。

追加のルール

  • 100バイト以下のBrainfuckプログラムがある場合、プログラムまたは機能は1分以内に終了する必要があります。

    つまり、入力に一致するまで、すべての有効なBrainfuckプログラムを反復処理することはできません。

  • 標準の規則が適用されます。


3
私はそれを8進数としてエンコードすることを考えていましたが、一致する括弧がこれを難しくしています。
DankMemes

空のプログラムは有効なBrainfuckプログラムですか?自然整数にもマップする必要がありますか?
orlp

9
なぜ投票するのですか?これは魅力的な質問であり、問題はたまたま素晴らしいゴルフのサイズと多様性を持っていることです。
-trichoplax

1
はい、空のプログラムを満たす上記の定義@orlp
デニス・

3
Brainfuckで書かれた答えを見るのを待っています...
マイケルハンプトン

回答:


16

Python 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のおかげで数バイト

仕組み:

これにより、有効なBFプログラムはすべて、有効なまたは無効なすべてのBFプログラムにマップされます。BFプログラム[は、1から1の比率で始まりません。その後、新しいプログラムは単純に8進数に変換されます。

マッピング式は次のとおりです。

  1. BFプログラムを3つの部分に分けます。最初の部分は、[文字のみで構成される最大のプレフィックスです。3番目の部分は、]文字のみで構成される最大の接尾辞です。2番目の部分は中央です。
  2. 最初の部分を処分します。これらは後で再計算できます。
  3. 2番目の部分のブラケット]と一致する3番目の部分のブラケットをすべて削除します[。これらは後で再計算することもできます。
  4. 2番目と3番目の部分を連結します。

この説明を理解していない場合は、ここからチャットで詳細な説明を見つけることができます。

参考までに、最初の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私はそれをゴルフするときにそれを修正します。
TheNumberOne

3
これは独創的です
誇り高いhaskeller

13

Python 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が、変数は次の3つの値のいずれかを取ります。

  • 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)    # '+'

注どのように我々は実際に再帰に接頭辞を使用していない-私たちはそれらを気にすべての変数介して取り込まれdoそして縮小入力プログラムs。上記の多くの繰り返しに気づくでしょう-これがキャッシュの出番であり、制限時間内に100文字のプログラムを処理できます。

ときはs空で、私たちは見て(d>=0 and o==0)1を返すかどうかを決定している、(それは/辞書的に早期に等しいだとプログラムが有効であるため、このプログラムをカウント)、または0(このプログラムはカウントされません)。

この接頭辞を持つプログラムはsよりも多くのsを持ち、したがって無効であるため、o < 0すぐにを含む状況はを返します。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)は、有効な長さのBrainfuckプログラムの数をカウントしnます。h(x)長さの番組をマップn[0..f(n)-1]し、g(x)問題の全単射ランキング関数です。

主なアイデアは、空でないプログラム[は、6つの非[]文字のいずれかで開始するか、1つで開始できるということです。前者の場合、一致する可能性のある場所を反復処理]し、囲まれた部分と末尾(末尾はに続く部分文字列を意味します])で再帰できます。後者の場合、末尾で再帰できます(末尾は最初の文字をドロップすることを意味します)。この推論は、カウントとランクの計算の両方に使用できます。

短いプログラムは、長いプログラムよりも常に低いランクを持ち、ブラケットパターンは二次的な決定要因です。非[]文字は「+-<> ,.」に従ってソートされます。(これは任意です)。

たとえば、n=4次の場合があります。

zxxx
[]xx
[x]x
[xx]

where zは非[]文字をx表し、任意の文字を表します。ただし]、がinitialと一致する必要があるという制限があります[。プログラムは、その順序に従って、xサブセクションで再帰的にランク付けされます。後者の場合、左側のセクションが右側のセクションよりも優先されます。ランクの計算は混合基数の数値システムに似てfおり、現在の「基数」を計算するために重要です。


4

この答えは、によって答えのための正式な証拠であるTheNumberOne列挙有効Brainf ** k個のプログラム、列挙が正しい理由細かいポイントを理解するには少し難しいことができ、。有効なプログラムでカバーされていない番号にマップする無効なプログラムがない理由を理解するのは簡単ではありません。

この回答全体を通して、大文字はプログラムを示すために使用され、小文字の変数は関数と整数に使用されます。〜は連結演算子です。

命題1:

関数fをその答えで説明されているプログラムとします。次に、すべてのプログラムUに対して、f(U)= f(V)のような有効なプログラム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))を構築します。

続行するには、3つの小さな事実が必要です。

クレーム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を2つの異なる有効なプログラムとします。次に、f(U)!= f(V)

これは、以前の提案と比較してかなり簡単です。

U2 = V2と仮定しましょう。ただし、UとVが異なる唯一の方法は、n []シンボルをそれぞれU1とU3に追加または削除することです。ただし、fは]サフィックス内の一致しないシンボルの数をカウントするため、これによりfの出力が変更されます。

したがって、U2!= V2です。

明らかに、これは矛盾につながります。U2とV2は、それぞれ文字通りf(U)とf(V)の出力に含まれているため、U2とU3が連結される「エッジ」を除いて、それらは異なることはできません。しかし、U2とV2の最初と最後のシンボルは、定義によって、[または]定義によって指定することはできません。これらは、それぞれU1、U3、V1、V3で許可される唯一のシンボルです。したがって、U2 = V2になります。QED

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.