Впровадити QuickSort у BrainF *** [закрито]


32

Як обговорювалося в кімнаті Lounge про переповнення стека:

якщо ви не можете реалізувати алгоритм Quicksort, що надається en.wikipedia.org/wiki/Quicksort будь-якою мовою, про яку ви володієте мінімальними знаннями, ви можете розглянути іншу професію. @sbi

але SBI також зазначив, що, можливо, BrainF *** був винятком.

Отже, ось головоломка / виклик: реалізуйте QuickSort у BrainF *** . Реалізація повинна

  • бути інтерпретованим цим та / або перекладачем (іми) тут (для великих сценаріїв)
  • реалізувати алгоритм, як описано у Вікіпедії - по можливості як місцевий вид
  • сортуйте наступний список цілих чисел: [0,4,6,4,2,3,9,2,3,6,5,3] та надрукуйте результат

Шукаючи трохи, я можу знайти одну реалізацію , але це 6 КБ (і складено з Haskell).
Пітер Тейлор

@Peter насправді реалізація головного мозку становить 474,2 К у архіві - це трохи більше, ніж я очікував (і занадто великий для он-лайн перекладача). Може бути , я повинен змінити мета перекладача .. (але я люблю , щоб побачити що - то рукописне)
Ronald

22
Б'юсь об заклад, що я міг би зробити сортування бульбашок замість цього, і ніхто, дивлячись на код, не знав би різниці ...
Пітер Олсон

1
Ідея @Keith полягає в тому, щоб реально реалізувати QuickSort, а не просто будь-який тип, який буде працювати ... :-)
Рональд

1
@ Петер кукурудзи: Ми виявимо сортування міхура за поганої роботи.
користувач невідомий

Відповіді:


55

BrainF * (697 байт)

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

Нижче наведена примітка із примітками. Для того, щоб слідкувати за тим, що повинно відбуватися під час його розробки, я використав нотацію коментаря, яка виглядає приблизно так:|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)

Пам'ять викладається з лівого зростаючої стопки розділів для обробки ліворуч, місцями подряпин у центрі, а масив сортується праворуч. Індексацією масиву обробляється переміщення «шини даних», що містить індекс та робочий простір через масив. Так, наприклад, 3-широка шина |i|data|0|A0|A1|A2стане |A0|i-1|data|0|A1|A2після переключення на одну. Розбиття виконується шляхом утримання шини між високим і низьким елементами.
Ось повна версія:

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
>>>>>>>>>>>[.>]

Я працював над подібним рішенням, але не міг повністю працювати. Дивовижна ідея зробити перегородку саме таким чином. Я витягував один елемент за один раз і замінював його, і він досить швидко став громіздким. Я також був 1,5 к, але ти і мене знищив на ефективності.
captncraig

1
Все в BF стає досить громіздким досить швидко :) Навіть, здавалося б, прості речі, як, наприклад, як зробити ефективний, if (i<j) {} else {}потребували декількох спроб вийти правильно. І крайні справи - це вбивці. Я не знаю, скільки разів я думав, що "лише ця штучка залишилася ...", а потім відкрив тестовий випадок, який спричинив ще кілька годин роботи. Я думаю, що можу скоротити її на кілька десятків символів, але я не впевнений, що хочу докласти зусиль.
AShelly

Одне слово: вау! Я, чесно кажучи, не вважав, що це по-людськи можливо. Я збираюся пропустити кілька входів через нього, щоб побачити, як це працює :-)
Рональд

Епічний! Просто епос!
vsz

єдине, що потрібно сказати - "святий f * ck!"
Math chiller

11

мозковий ебать (178 байт)

Навіть якщо мозковий ебать громіздкий, це допомагає працювати з зерном мови. Запитайте себе "Чи потрібно явно зберігати це значення у комірці?" Ви можете часто набирати швидкості та чіткості, роблячи щось більш тонке. І коли значення є індексом масиву (або довільним натуральним числом), воно не може вміщуватися в комірці. Звичайно, ви можете просто прийняти це як обмеження вашої програми. Але проектування вашої програми для обробки великих значень часто покращує її іншими способами.

Як завжди, моя перша робоча версія була вдвічі довшою, ніж мала бути - 392 байти. Численні модифікації та два-три основні переписування створили цю порівняно витончену 178-байтну версію. (Хоча кумедно сортування лінійного часу становить лише 40 байт.)

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

Вхідні значення розміщені кожні три комірки: для кожної клітинки (V) алью є (L) клітина абеля (використовується для навігації) та ще одна комірка для (S) місця стискання. Загальний макет масиву є

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

Спочатку всі клітинки L встановлюються на 1, щоб позначити частини масиву, які ще потребують сортування. Коли ми закінчимо розділення підмагістралі, ми розділимо його на менші підматриці, встановивши L-комірку його обертання на 0, а потім знайдемо крайню праву клітинку L, яка досі є 1, і розділимо цей підмасив наступним. Як не дивно, це все те, що нам потрібно, щоб правильно обробити рекурсивну обробку підрисів. Коли всі L комірки були нульовими, весь масив сортується.

Щоб розділити підмасив, ми втягуємо його найправіше значення у S-комірку, щоб вона діяла як зведена, і приводимо її (та відповідну порожню V-комірку) вліво, порівнюючи її між собою в підрядному масиві та замінюючи за необхідності. Зрештою, зворотний шар повертається назад, використовуючи той самий код підкачки (який зберігає 50 байт або близько того). Під час розподілу дві додаткові клітинки L утримуються на 0, щоб позначити дві клітини, які, можливо, потрібно буде поміняти один з одним; наприкінці розбиття лівий 0 зливається з 0 ліворуч від підмагістралі, а правий 0 закінчується позначенням його стрижня. Цей процес також залишає додатковий 1 в L-клітці праворуч від підмагістралі; основна петля починається і закінчується в цій комірці.

>+>>>>>,[>+>>,]>+[                      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
Дивовижно. Це набагато чистіше, коли ти працюєш з ідіомами BF, замість того, щоб намагатися змусити його діяти як процедурна мова, як я.
AShelly

Це є; але версія 4 на 392 байти теж була ідіоматичною. Це версія 39 або близько того. :)
Даніель Кристофані
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.