BrainF ***でのQuickSortの実装[終了]


32

スタックオーバーフローのラウンジルームで説明したように:

en.wikipedia.org/wiki/Quicksortに与えられたQuicksortアルゴリズムを最小限の知識しか持たない言語で実装できない場合は、別の職業を検討することをお勧めします。@sbi

しかし、SBIは、BrainF ***は例外かもしれないとも指摘しました。

そこで、ここにパズル/チャレンジがあります:QuickSortをBrainF ***に実装します。実装は

  • これおよび/またはここでのインタプリタによって解釈されます(大きなスクリプトの場合)
  • ウィキペディアで説明されているアルゴリズムを実装します-可能であれば、インプレースソートとして
  • 次の整数のリストをソートします:[0,4,6,4,2,3,9,2,3,6,5,3]および結果を出力します

少し調べてみると、1つの実装を見つけることができますが、6kBです(そしてHaskellからコンパイルされています)。
ピーターテイラー

実際、@ Peterはアーカイブ内のBrainfuckの実装は474.2 Kです。これは、予想よりも少し大きくなっています(オンラインインタープリターには大きすぎます)。たぶん私は..ターゲットインタプリタを変更する必要があります(私は考え愛する手書きの何かを参照する)
ロナルド・

22
私は...私は違いを知っているだろうコードを見て、ソートの代わりに泡と誰もが行うことができるに違いない
ピーター・オルソンに

1
アイデア...本当にその'LLの仕事だけではなく、任意の並べ替え、クイックソートを実装することです:-) @Keith
ロナルド

1
@Peter Of The Corn:悪いパフォーマンスでバブルソートを発見するでしょう。
ユーザー不明

回答:


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|A21つシフトした後になります。分割は、バスを高エレメントと低エレメントの間に保持することにより実行されます。
完全版は次のとおりです。

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つの要素を取り出して交換していましたが、すぐに非常に面倒になりました。私も1.5kでしたので、あなたも効率的に私を破壊しました。
captncraig

1
BFのすべては非常に迅速に面倒if (i<j) {} else {}になります。そして、エッジケースはキラーです。「この小さなものがあと1つだけ残っている」と思った回数がわからず、テストケースを発見したため、さらに数時間の作業が必要になりました。私はそれを数十文字減らすことができると思いますが、努力したいかどうかはわかりません。
AShelly

一言:すごい!正直なところ、それが人間的に可能だとは思わなかった。動作を確認するために、いくつかの入力を実行します:
ロナルド

エピック!ただ叙事詩!
-vsz

言うべき唯一のことは「神聖なf * ck!」です。
数学チラー

11

brainfuck (178バイト)

Brainfuckが面倒な場合でも、言語の粒度で作業するのに役立ちます。「この値をセルに明示的に保存する必要がありますか?」多くの場合、より微妙なことを行うことで速度と簡潔さを得ることができます。また、値が配列インデックス(または任意の自然数)の場合、セルに収まらない場合があります。もちろん、プログラムの制限としてそれを受け入れることもできます。ただし、大きな値を処理するようにプログラムを設計すると、多くの場合、他の方法で改善されます。

いつものように、私の最初の作業バージョンは必要な長さの2倍、つまり392バイトでした。多くの修正と2〜3回の主要な書き直しにより、この比較的優雅な178バイトバージョンが作成されました。(面白いことに、線形時間のソートはわずか40バイトです。)

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

入力値の間隔は3セルごとです。すべての(V)値セルには、(L)アベルセル(ナビゲーションに使用)と(S)隙間スペースにもう1つのセルがあります。配列の全体的なレイアウトは

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

最初はすべてのLセルが1に設定され、ソートが必要な配列の部分をマークします。サブアレイのパーティション分割が完了したら、ピボットのLセルを0に設定して小さなサブアレイに分割し、まだ1である右端のLセルを見つけて、そのサブアレイを次にパーティション分割します。奇妙なことに、これがサブアレイの再帰処理を適切に処理するために必要なすべての簿記です。すべてのLセルがゼロ化されると、配列全体がソートされます。

サブアレイを分割するには、その右端の値をSセルに入れてピボットとして機能させ、それ(および対応する空のVセル)を左にして、サブアレイ内の他の値と比較し、必要に応じて交換します。最後に、ピボットは同じスワップコードを使用してスワップバックされます(50バイト程度節約されます)。パーティション分割中、2つの余分なLセルは0に設定されたままになり、互いに交換する必要のある2つのセルをマークします。分割の終わりに、左の0はサブアレイの左の0と融合し、右の0はそのピボットをマークします。このプロセスでは、サブアレイの右側のLセルにも余分な1が残ります。メインループはこのセルで開始および終了します。

>+>>>>>,[>+>>,]>+[                      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

それは; しかし、392バイトのバージョン4も慣用的な頭痛の種でした。これはバージョン39程度です。:)
ダニエルクリストファニ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.