BrainF에서 가장 빠른 정렬 ***


15

BrainF ***에서 QuickSort 를 구현 한 후에 는 그렇게 빠르지 않다는 것을 깨달았습니다. 배열 인덱싱과 같은 일반 언어에서 O (1) 인 작업은 BF에서 상당히 길어집니다. 튜링 타르타르 에서 코딩 할 때 효율적인 정렬을위한 규칙의 대부분은 창 밖으로 던져 질 수 있습니다 .

"Fastest BrainF *** Sort Routine Ever"를 구현하기위한 도전이 여기 있습니다. 아래의 통역사를 사용하여 모든 출품작의 시간을 정합니다. 정수기는 부호없는 문자의 16K 테이프를 사용합니다. 테이프와 셀은 모두 한계를 초과하여 증가 / 증가 할 때 줄 바꿈됩니다. EOF를 읽으면 현재 셀에 0이 들어갑니다. 측정 된 시간에는 소스 파일을 구문 분석하는 시간과 모든 입력 파일을 처리하는 시간이 모두 포함됩니다. 가장 빠른 코드가 승리합니다.

테스트 벡터는 다음과 같은 정렬 사례를 테스트하도록 설계된 Ascii 파일 세트입니다.

  • 이미 정렬 된 목록 : "정렬"

    &#33;"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
    
  • 역순으로 정렬 된 목록 : "역방향"

    ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
    
  • 몇 가지 고유 값의 많은 사본으로 구성된 파일 : "onlynine"

    ibbkninbkrauickabcufrfckbfikfbbakninfaafafbikuccbariauaibiraacbfkfnbbibknkbfankbbunfruarrnrrrbrniaanfbruiicbuiniakuuiubbknanncbuanbcbcfifuiffbcbckikkfcufkkbbakankffikkkbnfnbncbacbfnaauurfrncuckkrfnufkribnfbcfbkbcrkriukncfrcnuirccbbcuaaifiannarcrnfrbarbiuk
    
  • 완전히 임의의 ASCII 파일 : "random"

    'fQ`0R0gssT)70O>tP[2{9' 0.HMyTjW7-!SyJQ3]gsccR'UDrnOEK~ca 'KnqrgA3i4dRR8g.'JbjR;D67sVOPllHe,&VG"HDY_'Wi"ra?n.5nWrQ6Mac;&}~T_AepeUk{:Fwl%0`FI8#h]J/Cty-;qluRwk|S U$^|mI|D0\^- csLp~`VM;cPgIT\m\(jOdRQu#a,aGI?TeyY^*"][E-/S"KdWEQ,P<)$:e[_.`V0:fpI zL"GMhao$C4?*x
    
  • 1..255 범위의 임의 파일 : "wholerange"

    öè—@œ™S±ü¼ÓuǯŠf΀n‚ZÊ,ˆÖÄCítÚDý^öhfF†¬I÷xxÖ÷GààuÈ©ÈÑdàu.y×€ôã…ìcÑ–:*‰˜IP¥©9Ä¢¬]Š\3*\®ªZP!YFõ®ÊÖžáîÓ¹PŸ—wNì/S=Ìœ'g°Ì²¬½ÕQ¹ÀpbWÓ³
    »y  »ïløó„9k–ƒ~ÕfnšÂt|Srvì^%ÛÀâû¯WWDs‰sç2e£+PÆ@½ã”^$f˜¦Kí•òâ¨÷ žøÇÖ¼$NƒRMÉE‹G´QO¨©l¬k¦Ó 
    

각 입력 파일은 최대 255 바이트입니다.

통역사가 있습니다. 이 콘솔 모드의 Windows 용으로 작성된,하지만 포트에 용이해야한다 : 단지 대체 read_time()sysTime_to_ms()플랫폼 별 등가물로.
용법: bftime program.bf infile1 [infile2 ...]

#include <windows.h>
#include <stdio.h>

#define MS_PER_SEC  1000.0f
#define MAXSIZE  (0x4000)
#define MAXMASK  (MAXSIZE-1)

typedef  __int64 sysTime_t;
typedef unsigned char Uint8;
typedef unsigned short Uint16;

typedef struct instruction_t {
   Uint8 inst;
   Uint16 pair;
} Instruction;

Instruction prog[MAXSIZE] = {0};
Uint8 data[MAXSIZE] = {0};
const Uint8 FEND = EOF;

sysTime_t read_time() {
    __int64 counts;
    QueryPerformanceCounter((LARGE_INTEGER*)&counts);
    return counts;
}

float sysTime_to_ms(sysTime_t timeIn) {
    __int64 countsPerSec;
    QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
    return (float)timeIn * MS_PER_SEC / (float)countsPerSec;
}

int main(int argc, char* argv[])
{
   FILE* fp;
   Uint8 c;
   Uint16 i = 0;
   Uint16 stack = 0;
   sysTime_t start_time;
   sysTime_t elapsed=0,delta;

   if (argc<3) exit(printf("Error: Not Enough Arguments\n"));
   fp = fopen(argv[1],"r");
   if (!fp) exit(printf("Error: Can't Open program File %s\n",argv[1]));

   start_time=read_time();
   while (FEND != (c = fgetc(fp)) && i <MAXSIZE) {
      switch (c)  {
      case '+': case '-': case ',': case '.': case '>': case '<':
         prog[++i].inst = c;
         break;
      case '[': 
         prog[++i].inst = c;
         prog[i].pair=stack;
         stack = i;
         break;
      case ']': 
         if (!stack) exit(printf("Unbalanced ']' at %d\n",i));
         prog[++i].inst = c;
         prog[i].pair=stack;
         stack = prog[stack].pair;
         prog[prog[i].pair].pair=i;
         break;
      }
   }
   if (stack) exit(printf("Unbalanced '[' at %d\n",stack));
   elapsed = delta = read_time()-start_time;
   printf("Parse Time: %f ms\n", sysTime_to_ms(delta));

   for (stack=2;stack<argc;stack++) {
      Instruction *ip = prog;
      fp = fopen(argv[stack],"r");
      if (!fp) exit(printf("Can't Open input File %s\n",argv[stack]));
      printf("Processing %s:\n", argv[stack]);
      memset(data,i=0,sizeof(data));

      start_time=read_time();
      //Run the program
      while (delta) {
         switch ((++ip)->inst) {
         case '+': data[i]++; break;
         case '-': data[i]--; break;
         case ',': c=getc(fp);data[i]=(FEND==c)?0:c; break;
         case '.': putchar(data[i]);  break;
         case '>': i=(i+1)&MAXMASK;   break;
         case '<': i=(i-1)&MAXMASK;   break;
         case '[': if (!data[i]) ip = prog+ip->pair; break;
         case ']': if (data[i])  ip = prog+ip->pair;  break;
         case 0: delta=0; break;
         }
      }
      delta = read_time()-start_time;
      elapsed+=delta;
      printf("\nProcessing Time: %f ms\n", sysTime_to_ms(delta));
   }
   printf("\nTotal Time for %d files: %f ms\n", argc-2, sysTime_to_ms(elapsed));
}

지금까지 결과

전체 벡터 세트의 5 회 평균 실행 시간은 다음과 같습니다.

 Author    Program      Average Time    Best Set          Worst Set
 AShelly   Quicksort    3224.4 ms       reverse (158.6)   onlynine (1622.4) 
 K.Randall Counting     3162.9 ms       reverse (320.6)   onlynine  (920.1)
 AShelly   Coinsort      517.6 ms       reverse  (54.0)   onlynine  (178.5) 
 K.Randall CountingV2    267.8 ms       reverse  (41.6)   random     (70.5)
 AShelly   Strandsort    242.3 ms       reverse  (35.2)   random     (81.0)

입력 요소의 범위는 무엇입니까?
Keith Randall

0 : 1-255를 제외하고 셀 범위입니다.
AShelly

당신은 내 시간을 다시해야합니다, 나는 그것을 조금 더 빠르게 만들었습니다.
Keith Randall

가장 최근의 것보다 2 배 이상 빠른 것 같습니다. 다른 사람에게 사용한 컴퓨터를 다시 사용할 때 공식 타이밍을 수행하겠습니다.
AShelly

답변:


9

여기 내 퀵 정렬보다 6 배 이상 빠른 정렬이 있습니다. O (N * m)이므로 m은 최대 입력 값이므로 전통적인 언어에서는 거의 의미가없는 알고리즘입니다. 입력을 수집 한 후에는 배열을 통과하여 셀> 0을 세고 각각을 줄입니다. 그런 다음 count결과 벡터 의 첫 번째 셀에 1을 더합니다 . 카운트가 0이 될 때까지 패스를 반복합니다.
BF :

Get Input
>,[>>+>,]   
Count values GT 0 and decrement each
<[<[<<<+>>>-]<[-<<+>>>]>[<]<<]
While count: add 1 to results
<[[[<<+>>-]<+<-]
Seek back to end of input
>[>>]>>>[>>>]
Repeat counting step
<<<[<[<<<+>>>-]<[-<<+>>>]>[<]<<]<]
Seek to far end of results and print in reverse order 
<[<<]>>[.>>]

C 등가 알고리즘 :

 uchar A[MAX]={0}; uchar R[MAX]={0}; int count,i,n=0;
 while (A[n++]=getchar()) ;
 do { 
   count = 0;
   for (i=0; i<n; i++) count += (A[i]) ? (A[i]-->0) : 0;
   for (i=0; i<count; i++) R[i]++; 
 } while (count>0);
 for (i=0; R[i]; i++) ;
 for (i--; i>=0; i--) putchar(R[i]);

다음은 2 배 빠른 것입니다. "스파게티 정렬"을 기반으로 합니다. 각 입력 길이만큼 1의 문자열을 내려 놓습니다. 각 셀의 값은 적어도 그 길이의 가닥 수를 나타냅니다. (따라서 [3,2,1,2]가됩니다 |4|0|3|0|1|0|0|). 그런 다음 가닥을 측정하기 시작하여 끝을 찾을 때마다 길이를 인쇄합니다.

>,[ [-[>>+<<-]>+>] <[<<]>,]   build strand of 1s for each input
+>[>+<-]>[                    while there are strands
  >[>+<<->-]                  do any strands end here?
  <[<<.>>-]                   print length of all that do  
  <<[>>+<<-]>>+>>]            shift right 1; inc length 

노골적인:

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

계산 종류를 노크하지 마십시오! 내가 한 번에 얻은 엄청난 승리로 인해 내가 가장 좋아하는 종류입니다. m이 작다고 알려진 경우 "빠른"알고리즘에 비해 엄청난 속도 향상을 얻을 수 있습니다. 마찬가지로 버블 정렬은 대부분 정렬 된 데이터에서 퀵 정렬보다 우선합니다. 모든 상황에 가장 적합한 ___ 알고리즘은 없습니다.
부스

나는 이것이 정확히 계산 종류라고 생각하지 않습니다. 당신의 의견은 저에게 더 많은 연구를 강요했습니다. 나는 이것이 구슬 정렬 과 비슷하다고 생각합니다 . 그러나 나는 그것이 옳지 않다고 확신합니다.
AShelly

아니, 네 말이 맞아 이것은 이상한 종류입니다. 링크 된 목록의 목록을 포함하는 일부 응용 프로그램에 유용 할 수 있습니다 ...하지만 그조차 모호합니다.
boothby

4
물리적 비유는 크기가 다른 N 개의 동전 더미가 있다는 것입니다. 다른 N 스택을위한 공간을 확보하십시오. 동전이있는 각 더미의 상단에서 동전 하나를 꺼내고 손이 비워 질 때까지 오른쪽에서 왼쪽으로 새 세트의 각 더미에 1을 추가합니다. 모든 원본 더미가 비워 질 때까지 반복하십시오. 이제 새 세트가 왼쪽에서 오른쪽으로 오름차순으로 정렬됩니다.
AShelly

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

이 알고리즘이 누구의 아이디어인지 기억이 나지 않습니다. 아마 Bertram Felgenhauer 's? 그것은 10 년 전에 Brainfuck Golf 콘테스트 # 2에 관한 토론에서 나왔습니다.

샘플 입력에서 가장 빠릅니다.

또한 길이가 <256 인 입력으로 제한되지 않고 임의로 긴 입력을 처리 할 수 ​​있습니다.

이 두 가지 모두 아래 알버트의 대답에도 해당됩니다. 이것에 대한 좋은 점은 입력 시간에서 실행 시간이 O (N)이라는 것입니다. 예,이 것은 실제로 선형 시간으로 실행됩니다. 이미 간식으로 255의 상수를 먹었습니다.


3

간단한 카운팅 정렬 구현. 각 버킷은 현재 입력, 마커 및 카운터가 입력에 나타나는 횟수를 포함하는 3 셀 너비입니다.

process input
,[

while input is not zero
[

decrement input
-

copy input over to next bucket
[->>>+<<<]

mark next bucket as not the first
>>>>+<

repeat until input is zero
]

increment count for this bucket
>>+

rewind using markers
<[-<<<]<

process next input
,]

generate output
>+[>[<-.+>-]<[->>>+<<<]>>>+]

코멘트를하지 않고:

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


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.