BrainF ***で最速のソート


15

BrainF ***でQuickSortを実装した後、おそらくそれほど速くないことに気付きました。通常の言語でO(1)である操作(配列のインデックス付けなど)は、BFでは大幅に長くなります。チューリングターピットでコーディングしている場合、効率的な並べ替えを行うルールのほとんどは、ウィンドウから除外できます

そのため、「Fastest BrainF *** Sort Routine Ever」を実装するための課題があります。以下のインタプリタを使用して、すべてのエントリの時間を計ります。インタプリタは、符号なし文字の16Kテープを使用します。テープとセルの両方は、制限を超えて前進/増分するとラップします。EOFを読み取ると、現在のセルに0が設定されます。測定時間には、ソースファイルの解析時間とすべての入力ファイルの処理時間の両方が含まれます。最も速いコードが勝ちます。

テストベクトルは、次のようなエッジケースのソートをテストするために設計されたAsciiファイルのセットになります。

  • すでにソートされたリスト:「ordered」

    &#33;"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
    
  • 逆ソートリスト:「逆」

    ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
    
  • いくつかの一意の値の多くのコピーで構成されるファイル:「onlynine」

    ibbkninbkrauickabcufrfckbfikfbbakninfaafafbikuccbariauaibiraacbfkfnbbibknkbfankbbunfruarrnrrrbrniaanfbruiicbuiniakuuiubbknanncbuanbcbcfifuiffbcbckikkfcufkkbbakankffikkkbnfnbncbacbfnaauurfrncuckkrfnufkribnfbcfbkbcrkriukncfrcnuirccbbcuaaifiannarcrnfrbarbiuk
    
  • 完全にランダムなASCIIファイル:「ランダム」

    '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)

入力要素の範囲はどのくらいですか?
キースランドール

0以外のセルの範囲:1〜255。
AShelly

あなたは私の時間を変更する必要があります、私はそれをかなり速くしました。
キースランドール

それは私の最新のものよりも2倍以上速いようです。
AShelly

回答:


9

これは、私のクイックソートより少なくとも6倍速いソートです。これは、mが最大入力値であるO(N * 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

いいえ、あなたは正しいです。これは奇妙な種類です。リンクされたリストのリストを含むいくつかのアプリケーションに役立つかもしれません...しかし、私はそれさえ疑っています。
ブースビー

4
物理的な例えは、サイズの異なるコインがNスタックあるということです。別のNスタック用のスペースを確保します。コインがある各スタックの一番上からコインを1枚取り出し、新しいセットの各スタックに右から左に1を追加して、手が空になるまで続けます。元のスタックがすべて空になるまで繰り返します。これで、新しいセットは左から右に昇順にソートされます。
AShelly

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

このアルゴリズムが誰のアイデアであったかは覚えていません。たぶん、バートラム・フェルゲンハウアーの?それは、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.