BrainF에서 QuickSort 구현 ***


32

스택 오버플로의 라운지 룸에서 논의한 바와 같이 :

최소한의 지식만으로 en.wikipedia.org/wiki/Quicksort에 Quicksort 알고리즘을 구현할 수없는 경우 다른 직업을 고려할 수 있습니다. s

그러나 SBI는 또한 BrainF ***가 예외라고 지적했다.

여기 퍼즐 / 도전이 있습니다 : BrainF *** 에서 QuickSort를 구현하십시오 . 구현은 반드시

  • 에 의해 해석 및 / 또는 인터프리터 (들)에 의해 여기 (대형 스크립트)
  • 가능한 경우 전체 정렬로 Wikipedia에 설명 된대로 알고리즘을 구현하십시오.
  • [0,4,6,4,2,3,9,2,3,6,5,3] 정수 목록을 정렬하고 결과를 인쇄하십시오.

조금 검색하면 하나의 구현 을 찾을 수 있지만 6kB입니다 (그리고 Haskell에서 컴파일 됨).
Peter Taylor

@Peter 실제로 brainfuck 구현은 아카이브 내부에서 474.2 K입니다. 이는 예상보다 약간 큽니다 (온라인 인터프리터에는 너무 큽니다). 어쩌면 내가 .. 대상 인터프리터를 변경해야합니다 (그러나 나는 것 좋아하는 뭔가 손으로 쓴보고)
로널드

22
대신 버블 정렬을 수행 할 수 있으며 코드를보고있는 사람은 차이점을 알 수 없습니다 ...
Peter Olson

1
@Keith 아이디어는 실제로 작동하는 모든 종류가 아닌 QuickSort를 구현하는 것입니다 ... :-)
Ronald

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|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.5k 였기 때문에 당신도 효율성을 떨어 뜨 렸습니다.
captncraig

1
BF의 모든 것은 매우 번거로워집니다 if (i<j) {} else {}. 그리고 가장 중요한 경우는 살인자입니다. 나는 "이 작은 일이 남았다 ..."라고 몇 번이나 생각한 다음 몇 시간 동안 처리해야하는 테스트 사례를 발견했다. 나는 그것을 12 문자로 줄일 수 있다고 생각하지만 노력을 기울이고 싶지는 않습니다.
AShelly

한마디 :와! 나는 그것이 인간적으로 가능하다고 생각하지 않았다. 나는 그것이 어떻게 작동하는지보기 위해 그것을 통해 몇 가지 입력을 실행할 것입니다 :-)
Ronald

서사시! 서사시!
vsz

말할 유일한 것은 "holy f * ck!"입니다.
Math chiller

11

brainfuck (178 바이트)

brainfuck가 번거 롭더라도 언어의 곡물을 다루는 데 도움이됩니다. "이 값을 셀에 명시 적으로 저장해야합니까?" 더 미묘한 일을함으로써 종종 속도와 결정을 얻을 수 있습니다. 그리고 값이 배열 인덱스 (또는 임의의 자연수) 인 경우 셀에 맞지 않을 수 있습니다 . 물론, 당신은 그것을 프로그램의 한계로 받아 들일 수 있습니다. 그러나 큰 값을 처리하도록 프로그램을 설계하면 다른 방식으로 개선하는 경우가 많습니다.

평소와 같이, 첫 번째 작업 버전은 392 바이트가 필요했던 것보다 2 배 길었습니다. 이 수정 된 178 바이트 버전은 수많은 수정 사항과 2 ~ 3 개의 주요 재 작성으로 작성되었습니다. (선명 하게 선형 시간 정렬 은 40 바이트에 불과합니다.)

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

입력 값은 3 개의 셀마다 간격이 있습니다. (V) alue 셀마다 (L) abel 셀 (탐색에 사용됨)과 (S) cratch 공간에 대해 하나 이상의 셀이 있습니다. 배열의 전체 레이아웃은

01000 SVLSVL ... SVL 000 000 ... ...

처음에는 모든 L 셀이 1로 설정되어 정렬이 필요한 배열 부분을 표시합니다. 하위 배열을 분할 한 후에는 피벗의 L 셀을 0으로 설정하여 하위 배열을 더 작은 하위 배열로 나눈 다음 여전히 1 인 가장 오른쪽의 L 셀을 찾고 다음에 해당 하위 배열을 분할합니다. 이상하게도 이것은 하위 배열의 재귀 처리를 올바르게 처리하는 데 필요한 모든 부기입니다. 모든 L 셀이 0이되면 전체 배열이 정렬됩니다.

하위 배열을 분할하기 위해 가장 오른쪽 값을 S 셀로 가져와 피벗 역할을하고 하위 배열의 서로 다른 값과 비교하여 필요에 따라 스와핑하여 피벗 역할을합니다. 마지막으로 동일한 스왑 코드 (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.