ระบุโปรแกรม Brainf ** k ที่ถูกต้อง


41

Golunar / Unaryเป็นวิธีการเข้ารหัสโปรแกรมBrainfuck ที่ถูกต้องทั้งหมดแต่ก็ไม่ได้เป็นการแจงนับเนื่องจากตัวเลขที่เป็นธรรมชาติส่วนใหญ่ไม่สอดคล้องกับโปรแกรมที่ถูกต้อง

สำหรับจุดประสงค์ของการท้าทายนี้ให้ใช้เทปที่ไม่มีที่สิ้นสุดเป็นทวีคูณและไม่มีความคิดเห็นเช่นโปรแกรม Brainfuck นั้นใช้ได้ถ้าหากมันประกอบไปด้วยตัวละคร<>+-.,[]และวงเล็บซ้ายและขวาเท่านั้น

ตัวอย่างเช่นโปรแกรมที่ว่างเปล่า,[+][-]., [>+<[--].]และ+[+[+][+[+]+]+]+.โปรแกรม brainfuck ที่ถูกต้องในขณะที่][และa[]ไม่ได้

งาน

เขียนโปรแกรมหรือฟังก์ชั่นที่ยอมรับโปรแกรม Brainfuck ที่ถูกต้องเป็นอินพุตและส่งคืนจำนวนธรรมชาติ ( 1 , 2 , 3 , …) โดยมีข้อ จำกัด ดังต่อไปนี้:

  • ผลลัพธ์ที่สร้างขึ้นจะต้องแตกต่างกันไปสำหรับโปรแกรม Brainfuck ที่ถูกต้องทั้งหมด

  • สำหรับทุกจำนวนธรรมชาติnจะต้องมีโปรแกรม brainfuck ที่ถูกต้องเมื่อมีให้เป็น input สร้างเอาท์พุทn

กฎเพิ่มเติม

  • รับโปรแกรม Brainfuck ที่มีขนาด 100 ไบต์หรือน้อยกว่าโปรแกรมหรือฟังก์ชันของคุณจะต้องเสร็จสิ้นภายในหนึ่งนาที

    ซึ่งหมายความว่าคุณไม่สามารถทำซ้ำในโปรแกรม Brainfuck ที่ถูกต้องทั้งหมดจนกว่าคุณจะตรงกับอินพุต

  • ใช้กฎมาตรฐานของ


3
ฉันคิดว่าจะเข้ารหัสมันเป็นเลขฐานแปด แต่วงเล็บที่ตรงกันนั้นทำให้มันยุ่งยาก
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: D

มันทำงานอย่างไร:

สิ่งนี้จะจับคู่โปรแกรม BF ที่ถูกต้องทั้งหมดเข้ากับโปรแกรม BF ที่เป็นไปได้ถูกต้องหรือไม่ถูกต้องทั้งหมดซึ่งไม่ได้ขึ้นต้นด้วย a [ในอัตราส่วนหนึ่งต่อหนึ่ง หลังจากนั้นโปรแกรมใหม่จะแปลงเป็นเลขฐานแปด

นี่คือสูตรการทำแผนที่:

  1. แยกโปรแกรม BF ออกเป็น 3 ส่วน ส่วนแรกเป็นคำนำหน้าที่ใหญ่ที่สุดประกอบด้วย[อักขระเท่านั้น ส่วนที่สามเป็น postfix ที่ใหญ่ที่สุดประกอบด้วย]ตัวอักษรเท่านั้น ส่วนที่สองคือตรงกลาง
  2. กำจัดส่วนแรก เหล่านี้สามารถคำนวณใหม่ได้ในภายหลัง
  3. ลบ]วงเล็บทั้งหมดในส่วนที่สามที่ตรงกับ[วงเล็บเหลี่ยมในส่วนที่สอง สิ่งเหล่านี้สามารถคำนวณใหม่ได้ในภายหลัง
  4. ต่อส่วนที่สองและสามเข้าด้วยกัน

ถ้าคุณไม่เข้าใจคำอธิบายนี้คุณสามารถหาคำอธิบายขยายในการแชทเริ่มต้นที่นี่

สำหรับการอ้างอิงต่อไปนี้เป็น 20 โปรแกรมแรก:

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

นี่คือโปรแกรมแรก 1,000 รายการ: http://pastebin.com/qykBWhmD
นี่คือโปรแกรมที่ฉันใช้ในการสร้าง: http://ideone.com/e8oTVl

นี่คือHello, World!:

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

Minib quble: คุณไม่สามารถแมปโปรแกรมกับ0ได้
เดนนิส

@Dennis โปรแกรมที่ว่างเปล่านับว่าเป็นโปรแกรมหรือไม่?
Beta Decay


@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ใช้หนึ่งในสามค่าที่ระบุ:

  • d = 1: คำนำหน้าปัจจุบันคือ lexicographically sก่อนหน้านี้กว่า เพิ่มโปรแกรมทั้งหมดด้วยคำนำหน้าและความยาว<= sนี้
  • d = -1: คำนำหน้าปัจจุบันคือ lexicographically sช้ากว่า เพิ่มโปรแกรมทั้งหมดด้วยคำนำหน้าและความยาว< sนี้
  • d = 0: คำนำหน้าปัจจุบันเป็นคำนำหน้าsดังนั้นเราอาจเปลี่ยนdเป็น 1 หรือ -1 ในภายหลัง

ตัวอย่างเช่นถ้าเรามีs = "[-]"และคำนำหน้าของเราในปัจจุบันคือp = "+"ตั้งแต่pช้ากว่าslexicographically เรารู้เพียง แต่จะเพิ่มโปรแกรมที่เริ่มต้นด้วยซึ่งเป็นอย่างเคร่งครัดสั้นกว่าps

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)จะตัดสินใจว่าจะคืนค่า 1 หรือไม่ (นับโปรแกรมนี้เนื่องจากโปรแกรมเร็ว / เท่ากับและโปรแกรมนั้นใช้ได้) หรือ 0 (ไม่นับโปรแกรมนี้)

การซิงก์ใด ๆ ที่o < 0ส่งคืนทันที0เนื่องจากโปรแกรมใด ๆ ที่มีคำนำหน้านี้มีมากกว่า]s [และไม่ถูกต้อง


เอาต์พุต 20 รายการแรกคือ:

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

ใช้ตัวอย่าง Hello World เดียวกันกับคำตอบของ @ TheNumberOne:

>>> 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 []ตัวอักษร ในกรณีก่อนหน้านี้เราสามารถวนซ้ำตำแหน่งที่เป็นไปได้ของการจับคู่]และเรียกคืนในส่วนที่ล้อมรอบและบนหาง (โดยที่ tail หมายถึงซับสตริงต่อไปนี้]) ในกรณีหลังเราสามารถเอาหางคืนมาได้ (โดยที่ท้ายหมายถึงปล่อยอักขระแรก) การใช้เหตุผลนี้สามารถใช้ได้ทั้งการนับและการคำนวณอันดับ

โปรแกรมที่สั้นกว่าจะมีอันดับต่ำกว่าโปรแกรมที่ยาวกว่าเสมอและรูปแบบของวงเล็บคือปัจจัยกำหนดรอง []อักขระที่ไม่ใช่จะถูกจัดเรียงตาม "+ - <>," (ซึ่งโดยพลการ)

ตัวอย่างเช่นn=4เรามีกรณีเหล่านี้:

zxxx
[]xx
[x]x
[xx]

ที่zย่อมาจากที่ไม่ใช่[]ตัวอักษรและxย่อมาจากตัวอักษรใด ๆ ภายใต้ข้อ จำกัด ที่มีการแข่งขันเริ่มต้น] [โปรแกรมได้รับการจัดอันดับตามลำดับนั้นและวนซ้ำในxส่วนย่อยโดยส่วนด้านซ้ายมีการจัดลำดับความสำคัญมากกว่าส่วนด้านขวาในกรณีหลัง การคำนวณอันดับจะคล้ายกับระบบตัวเลขผสมและfมีความสำคัญสำหรับการคำนวณ "radix" ปัจจุบัน


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 กรณีเล็ก ๆ น้อย ๆ

หากกรัม (U) <h (U) แล้วเราสามารถสร้าง V โดย prepending n = h (U) - กรัม (U) [สัญลักษณ์ เห็นได้ชัดว่า f (V) = f (U) เนื่องจาก[สัญลักษณ์ทั้งหมดในคำนำหน้าจะถูกลบออก

ตอนนี้พิจารณา g (U)> h (U) กำหนด T = U2 ~ U3 ถ้ากรัม (T) <= h (T) แล้วเราสามารถสร้าง V โดยการลบ n = กรัม (U) - h (U) [สัญลักษณ์

ดังนั้นเราสามารถสรุปได้ว่า 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) ตามลำดับจึงไม่สามารถแตกต่างได้ยกเว้นที่ 'edge' สถานที่ที่ U2 ต่อกับ U3 แต่สัญลักษณ์แรกและสุดท้ายของ U2 และ V2 ไม่สามารถ[หรือ]ตามความหมายได้ในขณะที่สัญลักษณ์เหล่านั้นเป็นเพียงสัญลักษณ์ที่อนุญาตใน U1, U3, V1, V3 ตามลำดับและตามลำดับอีกครั้ง ดังนั้นเราจึงได้รับ U2 = V2 QED

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.