Triển khai QuickSort trong BrainF *** [đã đóng]


32

Như đã thảo luận trong phòng Lounge trên Stack Overflow:

nếu bạn không thể thực hiện thuật toán Quicksort được cung cấp en.wikipedia.org/wiki/Quicksort bằng bất kỳ ngôn ngữ nào bạn có kiến ​​thức tối thiểu, bạn có thể muốn xem xét một nghề khác. @sbi

nhưng SBI cũng lưu ý rằng có lẽ BrainF *** là một ngoại lệ.

Vì vậy, đây là câu đố / thách thức: triển khai QuickSort trong BrainF *** . Việc thực hiện phải

  • được giải thích bởi điều này và / hoặc bởi (các) thông dịch viên ở đây (đối với các tập lệnh lớn)
  • triển khai thuật toán như được mô tả trên Wikipedia - nếu có thể dưới dạng sắp xếp tại chỗ
  • sắp xếp danh sách các số nguyên sau: [0,4,6,4,2,3,9,2,3,6,5,3] và in kết quả

Tìm kiếm xung quanh một chút tôi có thể tìm thấy một triển khai , nhưng đó là 6kB (và được biên dịch từ Haskell).
Peter Taylor

@Peter thực tế việc triển khai brainfuck là 474,2 K bên trong kho lưu trữ - lớn hơn một chút so với tôi dự kiến ​​(và quá lớn đối với trình thông dịch trực tuyến). Có lẽ tôi nên thay đổi trình thông dịch mục tiêu .. (nhưng tôi rất thích xem một cái gì đó viết tay)
Ronald

22
Tôi cá là tôi có thể thực hiện sắp xếp bong bóng thay vào đó và không ai nhìn vào mã sẽ biết sự khác biệt ...
Peter Olson

1
@ Ý tưởng là thực sự triển khai QuickSort, không phải bất kỳ loại nào sẽ hoạt động ... :-)
Ronald

1
@Peter Of The Corn: Chúng tôi sẽ phát hiện ra một loại bong bóng bởi hiệu suất kém.
người dùng không xác định

Câu trả lời:


55

BrainF * (697 byte)

>>>>>>>>,[>,]<[[>>>+<<<-]>[<+>-]<+<]>[<<<<<<<<+>>>>>>>>-]<<<<<<<<[[>>+
>+>>+<<<<<-]>>[<<+>>-]<[>+>>+>>+<<<<<-]>[<+>-]>>>>[-<->]+<[>->+<<-[>>-
<<[-]]]>[<+>-]>[<<+>>-]<+<[->-<<[-]<[-]<<[-]<[[>+<-]<]>>[>]<+>>>>]>[-<
<+[-[>+<-]<-[>+<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<<<<<<]<<[>>+<<-]>[>[>+>
>+<<<-]>[<+>-]>>>>>>[<+<+>>-]<[>+<-]<<<[>+>[<-]<[<]>>[<<+>[-]+>-]>-<<-
]>>[-]+<<<[->>+<<]>>[->-<<<<<[>+<-]<[>+<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]
<<]>[[-]<<<<<<[>>+>>>>>+<<<<<<<-]>>[<<+>>-]>>>>>[-[>>[<<<+>>>-]<[>+<-]
<-[>+<-]>]<<[[>>+<<-]<]]>]<<<<<<-]>[>>>>>>+<<<<<<-]<<[[>>>>>>>+<<<<<<<
-]>[<+>-]<+<]<[[>>>>>>>>+<<<<<<<<-]>>[<+>-]<+<<]>+>[<-<<[>+<-]<[<]>[[<
+>-]>]>>>[<<<<+>>>>-]<<[<+>-]>>]<[-<<+>>]>>>]<<<<<<]>>>>>>>>>>>[.>]

Dưới đây là một phiên bản chú thích. Để theo dõi những gì đáng lẽ sẽ xảy ra trong khi phát triển nó, tôi đã sử dụng một ký hiệu nhận xét giống như thế này:|a|b=0|c=A0|@d|A0|A1|```|

|a| represents a named cell
|b=X| means we know the cell has value X, where X can be a constant or a variable name
|@d|  means the data pointer is in this cell
|A0|A1|```| is variable length array. (using ``` for ... because . is a command)

Bộ nhớ được bố trí với một ngăn phân vùng phát triển bên trái để xử lý ở bên trái, một khoảng trống ở giữa và mảng được sắp xếp ở bên phải. Lập chỉ mục mảng được xử lý bằng cách di chuyển một "bus dữ liệu" chứa chỉ mục và không gian làm việc thông qua mảng. Vì vậy, ví dụ, một chiếc xe buýt 3 chiều |i|data|0|A0|A1|A2, sẽ trở thành |A0|i-1|data|0|A1|A2sau khi thay đổi một chiếc. Việc phân vùng được thực hiện bằng cách giữ bus giữa các yếu tố cao và thấp.
Đây là phiên bản đầy đủ:

Get input
>>>>>>>> ,[>,]                      |A0|A1|```|An|@0|
Count items
<[ [>>>+<<<-]>[<+>-]<+ <]  |@0|n|0|0|A0|A1|```
Make 8wide data bus w/ stack on left
>[<<<<<<<<+>>>>>>>>-]  ```|K1=n|K0=0|Z=0|a|b|c|d|e|@f|g|X=0|A0|A1|```
K1 and K0 represent the first index to process (I) and one past the last (J)
Check if still partitions to process
<<<<<<<<[
  Copy K1 to a&c via Z
  [>>+>+>>+<<<<<-]>>[<<+>>-] ```|K1=J|K0=I|@Z=0|a=J|b|c=J|d|e|f|g|X=0|A0|A1|```
  Copy K0 to b&d via Z
  <[>+>>+>>+<<<<<-]>[<+>-] ```|K1|K0|@Z=0|a=J|b=I|c=J|d=I|e|f|g|X=0|A0|A1|```
  Check if J minus I LE 1 : Subtract d from c
  >>>>[-<->]                    |a=J|b=I|c=JminusI|@d=0|e|f|g|
  d= c==0; e = c==1
  +<[>- >+<<-[>>-<<[-]]]        |a=J|b=I|@c=0|d=c==0|e=c==1|f|g|
  if d or e is 1 then J minus I LE 1: partition empty
  >[<+>-]>[<<+>>-]<+<      |a=J|b=I|@c=isEmpty|d=1|e=0|f|g|
  If Partition Empty;
  [->-                      |a=J|b=I|@c=0|d=0|c=0|f|g|
    pop K0: Zero it and copy the remaining stack right one; inc new K0
    <<[-]<[-]<<[-]<[[>+<-]<]>>[>]<+    ``|K1|@Z=0|a=J|b=I|c=0|d=0|e|f|g|
  Else:
  >>>>]>[-                   Z|a=J|b=I|c=isEmpty=0|@d=0|e|f|g|X|A0|A1
    Move Bus right I plus 1 frames; leaving first element to left
    <<+[ -[>+<-]<-[>+<-]>>>>>>>>      (dec J as we move)
      [<<<<<<<<+>>>>>>>>-]<<<<<< ]      Z|Ai|a=J|@b=0|c=0|d|e|f|g|X|Aq
    first element becomes pivot Ap; store in b
    <<[>>+<<-]            Z|@0|a=J|b=Ap|c=0|d|e|f|g|X|Aq
    While there are more elements (J GT 0);
    >[                    Z|0|@a=J|b=Ap|c=0|d|e|f|g|X|Aq
      copy Ap to e via c
      >[>+>>+<<<-]>[<+>-]  Z|0|a=J|b=Ap|@c=0|d=0|e=Ap|f|g|X=0|Aq
       copy Aq to g via X
      >>>>>>[<+<+>>-]<[>+<-] |c|d=0|e=Ap|f|g=Aq|@X=0|Aq
      Test Aq LT Ap:  while e; mark f; clear it if g 
      <<<[ >+>[<-]<[<]           |@d=0|e|f=gLTe|g|
        if f: set d and e to 1; dec e and g 
        >>[<<+>[-]+>-]>-<<-]
      set g to 1; if d: set f 
      >>[-]+<<< [->>+<<]
      If Aq LT Ap move Aq across Bus
      >>[->- <<<<<[>+<-] <[>+<-] >>>>>>>>
        [<<<<<<<<+>>>>>>>>-] <<]  Z|0|Aq|a=J|b=Ap|c|d|e|@f=0|g=0|X=0|Ar
      Else Swap AQ w/ Aj: Build a 3wide shuttle holding J and Aq                
      >[[-] <<<<<<[>>+>>>>>+<<<<<<<-]>>[<<+>>-] |@c=0|d|e|f=0|g=0|X=J|Aq|Ar|```
      If J then dec J
      >>>>>[-
        & While J shuttle right
        [>>[<<<+>>>-]<[>+<-]<-[>+<-]>] |a=J|b=Ap|c|d|e|f|Ar|```|Aj|g=0|@X=0|Aq|
        Leave Aq out there and bring Aj back
        <<[ [>>+<<-] < ]              |a=J|b=Ap|c|d|e|@f=0|g|X=0|Ar|```|Aj|Aq|
      ]>]
    Either bus moved or last element swapped; reduce J in either case
    <<<<<<-]                 |Aq|@a=0|b=Ap|c|d|e|f|g|X|Ar|```|
    Insert Ap To right of bus
    >[>>>>>>+<<<<<<-]        |Aq|a=0|@b=0|c|d|e|f|g|Ap|Ar|```|
    Move the bus back to original location tracking pivot location
    <<[ [>>>>>>>+<<<<<<<-]>[<+>-]<+ <]     
    <[ [>>>>>>>>+<<<<<<<<-]>>[<+>-]<+ <<] |K1|K0|@Z=0|a=0|b=p|c|d|e|f|g|X|Ar|```
    if p is not 0:  put new partition on stack between K0 and K1:
    >+>[<-                                 |K1|K0|Z=0|@a=pEQ0|b=p|
      move K0 to Z; search for last K
      <<[>+<-] <[<]                           |@0|Kn|```|K1|0|Z=K0|a=0|b=p| 
      shift left until return to 0 at K0;
      >[ [<+>-] >]                            |Kn|```|K1|0|@0|Z=K0|a=0|b=p|
      put p one left of there making it K1; restore K0 from Z;
      >>>[<<<<+>>>>-]<<[<+>-]                 |Kn|```|K2|K1=p|K0|@Z=0|a=0|b=0|
    else increment K0 (special case when first partition empty) 
    >>]<[- <<+>>]              
  >>>]  End if !empty
<<<<<<] End If Partitions remaining   @K1=0|K0=0|Z=0|a|b|c|d|e|f|g|X=0|A0|A1|```
Print the Results
>>>>>>>>>>>[.>]

Tôi đã làm việc trên một giải pháp tương tự nhưng không thể làm cho nó hoạt động được. Ý tưởng tuyệt vời để làm phân vùng theo cách đó. Tôi đã rút ra một yếu tố tại một thời điểm và thay thế nó, và nó trở nên khá cồng kềnh khá nhanh. Tôi cũng được 1,5k vào nó, vì vậy bạn cũng phá hủy tôi về hiệu quả.
captncraig

1
Mọi thứ trong BF trở nên cồng kềnh khá nhanh chóng :) Ngay cả những điều tưởng chừng đơn giản như làm thế nào để thực hiện một cách hiệu quả cũng if (i<j) {} else {}phải mất vài lần để làm đúng. Và các trường hợp cạnh là kẻ giết người. Tôi không biết bao nhiêu lần tôi nghĩ rằng "chỉ một điều nhỏ này còn lại ..." và sau đó phát hiện ra một trường hợp thử nghiệm khiến một vài giờ làm việc phải xử lý. Tôi nghĩ rằng tôi có thể giảm nó vài chục ký tự, nhưng tôi không chắc là tôi muốn nỗ lực.
HỎI

Một từ: wow! Tôi thực sự không nghĩ rằng đó là con người có thể. Tôi sẽ chạy một vài đầu vào thông qua nó để xem nó hoạt động như thế nào :-)
Ronald

Sử thi! Chỉ cần sử thi!
vsz

điều duy nhất để nói là "thánh f * ck!"
làm lạnh toán học

11

cân não (178 byte)

Ngay cả khi brainfuck là cồng kềnh, nó sẽ giúp làm việc với các hạt của ngôn ngữ. Hãy tự hỏi mình "Tôi có phải lưu trữ giá trị này một cách rõ ràng trong một tế bào không?" Bạn thường có thể đạt được tốc độ và sự điều chỉnh bằng cách làm một cái gì đó tinh tế hơn. Và khi giá trị là một chỉ số mảng (hoặc số tự nhiên tùy ý), nó có thể không phù hợp trong một ô. Tất nhiên, bạn chỉ có thể chấp nhận đó là một giới hạn của chương trình của bạn. Nhưng thiết kế chương trình của bạn để xử lý các giá trị lớn thường sẽ làm cho nó tốt hơn theo những cách khác.

Như thường lệ, phiên bản làm việc đầu tiên của tôi dài gấp đôi, nó cần phải là 392 byte. Nhiều sửa đổi và hai hoặc ba bản viết lại chính đã tạo ra phiên bản 178 byte tương đối duyên dáng này. (Mặc dù sắp xếp thời gian tuyến tính chỉ là 40 byte.)

>+>>>>>,[>+>>,]>+[--[+<<<-]<[[<+>-]<[<[->[<<<+>>>>+<-]<<[>>+>[->]<<[<]
<-]>]>>>+<[[-]<[>+<-]<]>[[>>>]+<<<-<[<<[<<<]>>+>[>>>]<-]<<[<<<]>[>>[>>
>]<+<<[<<<]>-]]+<<<]]+[->>>]>>]>[brainfuck.org>>>]

Các giá trị đầu vào được đặt cách nhau mỗi ba ô: đối với mỗi ô (V) alue, có một ô abel (L) (được sử dụng để điều hướng) và thêm một ô cho không gian khóa (S). Bố cục tổng thể của mảng là

0 1 0 0 0 SVLSVL ... SVL 0 0 0 0 0 0 ...

Ban đầu tất cả các ô L được đặt thành 1, để đánh dấu các phần của mảng vẫn cần sắp xếp. Khi chúng tôi phân vùng xong một phân đoạn, chúng tôi chia nó thành các phân đoạn nhỏ hơn bằng cách đặt ô L của trục của nó thành 0, sau đó xác định ô L bên phải vẫn là 1 và phân vùng tiếp theo. Điều kỳ lạ, đây là tất cả các sổ sách kế toán mà chúng ta cần để xử lý đúng cách xử lý đệ quy của các tập hợp con. Khi tất cả các ô L đã được zero, toàn bộ mảng được sắp xếp.

Để phân vùng một phân đoạn, chúng ta kéo giá trị ngoài cùng bên phải của nó vào một ô S để đóng vai trò là trục và đưa nó (và ô V trống tương ứng) sang trái, so sánh nó với các giá trị khác trong phân đoạn phụ và hoán đổi khi cần. Cuối cùng, trục được hoán đổi trở lại, sử dụng cùng một mã trao đổi (giúp tiết kiệm 50 byte hoặc hơn). Trong quá trình phân vùng, hai ô L bổ sung được giữ thành 0, để đánh dấu hai ô có thể cần được hoán đổi với nhau; ở cuối phân vùng, 0 bên trái sẽ hợp nhất với 0 ở bên trái của phân đoạn phụ và 0 bên phải sẽ kết thúc đánh dấu trục của nó. Quá trình này cũng để lại thêm 1 trong ô L ở bên phải của phân đoạn; vòng lặp chính bắt đầu và kết thúc tại ô này.

>+>>>>>,[>+>>,]>+[                      set up; for each subarray:
    --[+<<<-]<[                         find the subarray; if it exists:
        [<+>-]<[                        S=pivot; while pivot is in S:
            <[                          if not at end of subarray
                ->[<<<+>>>>+<-]         move pivot left (and copy it) 
                <<[>>+>[->]<<[<]<-]>    move value to S and compare with pivot
            ]>>>+<[[-]<[>+<-]<]>[       if pivot greater then set V=S; else:
                [>>>]+<<<-<[<<[<<<]>>+>[>>>]<-]     swap smaller value into V
                <<[<<<]>[>>[>>>]<+<<[<<<]>-]        swap S into its place
            ]+<<<                       end else and set S=1 for return path
        ]                               subarray done (pivot was swapped in)
    ]+[->>>]>>                          end "if subarray exists"; go to right
]>[brainfuck.org>>>]                    done sorting whole array; output it

1
Tuyệt vời. Nó sạch sẽ hơn rất nhiều khi bạn làm việc với các thành ngữ của BF, thay vì cố ép nó hành động như một ngôn ngữ thủ tục, như tôi đã làm.
AShelly

Nó là; nhưng phiên bản 4 ở 392 byte cũng là một bộ não thành ngữ. Đây là phiên bản 39 hoặc hơn. :)
Daniel Cristofani
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.