BrainF'de En Hızlı Sıralama ***


15

QuickSort'u BrainF ***' de uyguladıktan sonra , muhtemelen bu kadar hızlı olmadığını fark ettim. Normal dillerde O (1) olan işlemler (dizi indeksleme gibi) BF'de önemli ölçüde daha uzundur. Bir Turing tarpitinde kodlama yaparken etkili bir sıralama yapan kuralların çoğu pencereden atılabilir.

İşte burada "En Hızlı BrainF *** Sırala Rutini" uygulamak için bir meydan okuma. Aşağıdaki yorumlayıcıyı kullanarak tüm girişleri zamanlayacağım. Intepreter 16K bant imzasız karakterler kullanır. Sınırları aştığında / artırıldığında hem bant hem de hücreler sarılır. EOF'un okunması mevcut hücreye 0 koyar. Ölçülen süre hem kaynak dosyasını ayrıştırma süresini hem de tüm girdi dosyalarını işleme süresini içerir. En hızlı kod kazanır.

Test vektörü, sıralama vakalarını test etmek için tasarlanmış bir dizi Ascii dosyası olacaktır.

  • Zaten sıralanmış bir liste: "sipariş edildi"

    &#33;"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
    
  • Ters sıralı liste: "ters"

    ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
    
  • Birkaç benzersiz değerin birçok kopyasından oluşan bir dosya: "onlynine"

    ibbkninbkrauickabcufrfckbfikfbbakninfaafafbikuccbariauaibiraacbfkfnbbibknkbfankbbunfruarrnrrrbrniaanfbruiicbuiniakuuiubbknanncbuanbcbcfifuiffbcbckikkfcufkkbbakankffikkkbnfnbncbacbfnaauurfrncuckkrfnufkribnfbcfbkbcrkriukncfrcnuirccbbcuaaifiannarcrnfrbarbiuk
    
  • Tamamen rastgele bir ascii dosyası: "rastgele"

    '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 aralığında rastgele bir dosya: "bütünlük"

    öè—@œ™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¦Ó 
    

Her girdi dosyasında en fazla 255 bayt bulunur.

İşte tercüman. Bu konsol mod Windows için yazılmış, ama limana kolay olmalıdır: Sadece yerini read_time()ve sysTime_to_ms()platforma özel eşdeğerleriyle.
Kullanımı: 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));
}

Şimdiye Kadar Sonuçlar

İşte tam vektör kümesinin 5 çalışmasının ortalama süresi:

 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)

Giriş elemanlarının menzili nedir?
Keith Randall

0: 1-255 hariç, hücrelerin aralığıdır.
AShelly

benimkini tekrar kullanmalısın, biraz daha hızlı yaptım.
Keith Randall

En yakın zamandan 2 kat daha hızlı görünüyor - diğerleri için kullandığım makineye geri döndüğümde resmi zamanlamayı yapacağım.
AShelly

Yanıtlar:


9

İşte benim çabuk benim en az 6 kat daha hızlı bir tür. Geleneksel bir dilde çok az anlam ifade edecek bir algoritmadır, çünkü m'nin maksimum giriş değeri olduğu O (N * m). Girdiyi topladıktan sonra, diziden geçer,> 0 hücrelerini sayar ve sonra her birini azaltır. Daha sonra countsonuç vektörünün ilk hücrelerine 1 ekler . Sayı 0 olana kadar geçişleri tekrarlar.
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 eşdeğer algoritması:

 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]);

İşte 2 kat daha hızlı. Gevşek bir şekilde "spagetti sıralama" dayanmaktadır : her giriş sürece 1s dizesi bırakır. Her bir hücredeki değer, en azından o kadar uzun iplik sayısını temsil eder. (Böylece [3,2,1,2] olur |4|0|3|0|1|0|0|). Daha sonra ipleri 'ölçmeye' başlar ve bir ucunu her bulduğunda uzunluğu yazdırır.

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

Çiğ:

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

Sayma vuruşlarını çalmayın! Bu benim en sevdiğim tür, ondan bir kez aldığım büyük bir kazanç nedeniyle: m küçük olduğu biliniyorsa, aksi takdirde "hızlı" algoritmalar üzerinde büyük hızlanmalar elde edebilirsiniz. Benzer şekilde, kabarcık sıralaması çoğunlukla sıralanan verilerde çabuk sıralama yapar. Hiç kimse ___ algoritma her bağlam için en iyisi değildir.
boothby

Bunun tam olarak bir sayım olduğunu düşünmüyorum. Yorumunuz beni biraz daha araştırma yapmaya zorladı. Bence bu daha çok bir boncuk gibidir . Ama bunun doğru olduğundan bile emin değilim.
AShelly

Hayır, haklısın. Bu garip bir tür. Bağlantılı listelerin listelerini içeren bazı uygulamalar için yararlı olabilir ... ama ben bile şüpheliyim.
boothby

4
Fiziksel benzetme, farklı boyutlarda N yığın paraya sahip olmanızdır. Başka bir N yığını için boşluk bırakın. Madeni paraları olan her yığının üstünden bir jeton alırsınız ve ardından eliniz boşalana kadar yeni kümedeki her kümeye 1'den sağa sola eklersiniz. Tüm orijinal yığınlar boşalana kadar tekrarlayın. Şimdi yeni set soldan sağa doğru artan şekilde sıralandı.
AShelly

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

Bu algoritmanın kim olduğu fikrini hatırlamıyorum. Belki Bertram Felgenhauer? On yıl önce Brainfuck Golf yarışması # 2'deki tartışmalardan geldi.

Bu, örnek girişlerde en hızlı olanıdır.

Ayrıca, uzunluk <256 olan girişlerle de sınırlı değildir, ancak keyfi olarak uzun girişleri de işleyebilir.

Her ikisi de aşağıda Albert'ın cevapları için de geçerliydi. Bununla ilgili güzel olan şey, çalışma süresinin giriş uzunluğunda O (N) olmasıdır. Evet, bu şey aslında doğrusal zamanda çalışır. Zaten bir atıştırmalık olarak 255 sabit bir faktör yedi.


3

Basit bir sayma sıralama uygulaması. Her bir kova, geçerli girdi, bir işaretçi ve sayacın girişte kaç kez göründüğünü içeren 3 hücre genişliğindedir.

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

yorumsuz:

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


2
>>+>,[>+>,]<[[<-<]>+>+[>]>[[-<<[[>>+<<-]<]>>]>-.+[>]>]<<]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.