CでのGolfed + Fastソート


11

[ 最新の更新:ベンチマークプログラムと暫定版が利用可能、以下を参照]

そこで、古典的なアプリケーションであるソートを使用して、速度と複雑さのトレードオフをテストしたいと思います。

浮動小数点数の配列を昇順で並べ替えるANSI C関数を記述します。

あなたは使用することはできません任意のライブラリ、システムコール、マルチスレッドまたはインラインASMを。

エントリは、コード長パフォーマンスの2つのコンポーネントで判断されます。スコアは次のとおりです。エントリは、長さ(空白なしの#charactersのログなので、フォーマットを維持できます)およびパフォーマンス(ベンチマークの#secondsのログ)、および各間隔[best、worst]に線形に正規化されます0,1]。プログラムの合計スコアは、2つの正規化されたスコアの平均になります。最低スコアが勝ちます。ユーザーごとに1つのエントリ。

ソートは(最終的に)適切に行われる必要があります(つまり、入力配列には戻り時にソートされた値が含まれる必要があります)。名前を含む次の署名を使用する必要があります。

void sort(float* v, int n) {

}

カウントされる文字:sort関数に含まれる文字、署名が含まれ、それに呼び出される追加の関数(ただし、テストコードは含まれません)。

プログラムはfloat、長さ> = 0、最大2 ^ 20の任意の数値と配列を処理する必要があります。

sortテストプログラムにプラグインとその依存関係をプラグインし、GCCでコンパイルします(派手なオプションはありません)。たくさんの配列をそこに入れて、結果の正確さと合計実行時間を確認します。テストは、Ubuntu 13のIntel Core i7 740QM(Clarksfield)で実行されます。
アレイの長さは、許容される範囲全体に渡り、短いアレイの密度が高くなります。値はランダムで、ファットテール分布(正と負の両方の範囲)になります。重複した要素はいくつかのテストに含まれます。
テストプログラムはこちらから入手できます。https : //gist.github.com/anonymous/82386fa028f6534af263
提出物をとしてインポートしuser.cます。TEST_COUNT実際のベンチマークのテストケースの数()は3000です。質問のコメントでフィードバックを提供してください。

締め切り:3週間(2014年4月7日、16:00 GMT)。2週間後にベンチマークを掲載します。
競合他社にコードを渡さないように、締め切り近くに投稿することをお勧めします。

ベンチマーク発行時点での予備結果:
いくつかの結果があります。最後の列は、スコアをパーセンテージで示しており、高いほど良いので、ジョニーケージが1位になります。他のアルゴリズムよりも桁違いに遅いアルゴリズムをテストのサブセットで実行し、時間を推定しました。Cのものqsortは比較のために含まれています(ジョニーの方が高速です!)。終了時に最終比較を行います。

ここに画像の説明を入力してください


3
ベンチマークを提供できますか?異なるソート関数は、データの性質に基づいて異なる方法で実行されます。たとえば、バブルソートは、小さな配列のstdlibクイックソートよりも高速です。ベンチマークに合わせて最適化することもできます。
クラウディウ

@Claudiu私はかつて、クイックソートの素敵な短いバージョンを見たことがあります。しかし、いくつかの要素が同じ場合、それは絶対的なカタツムリのペースで実行されました。私は、ソートされた/部分的にソートされた配列のピボットの悪い選択の既知の問題について話していません。テストデータは完全にランダムにシャッフルされました。この特定のバージョンでは、重複が好きではありませんでした。奇妙ですが、本当です。
レベルリバーセント

3
PPCGへようこそ!言語固有の課題を禁止していませんが、可能な限り言語に依存しない方法で質問を作成することを強くお勧めします。次の質問でそれを考慮し、これを楽しんでください!
ジョナサンヴァンマトレ

1
@steveverrill:私は従わない。とにかく0から1にスケーリングするため、ユニットが何であるかは関係ありません。最小が1時間、最大が3時間の場合、最小60分、最大180分、90分かかるかどうかに関係なく、1.5時間かかるものは0.25になります
Claudiu

1
OPはインラインアセンブリなしとだけ言った-組み込み関数については何も言わなかった。
ポールR 14年

回答:


6

150文字

クイックソート。

/* 146 character.
 * sizeup 1.000; speedup 1.000; */
#define REC_SIZE    \
    sort(l, v+n-l); \
    n = l-v;

/* 150 character.
 * sizeup 1.027; speedup 1.038; */
#define REC_FAST  \
    sort(v, l-v); \
    n = v+n-l;    \
    v = l;

void sort(float* v, int n)
{
    while ( n > 1 )
     {
       float* l = v-1, * r = v+n, x = v[n/2], t;
L:
       while ( *++l < x );
       while ( x < (t = *--r) );

       if (l < r)
        {
          *r = *l; *l = t;
          goto L;
        }
       REC_FAST
     }
}

圧縮。

void sort(float* v, int n) {
while(n>1){float*l=v-1,*r=v+n,x=v[n/2],t;L:while(*++l<x);while(x<(t=*--r));if(l<r){*r=*l;*l=t;goto L;}sort(v,l-v);n=v+n-l;v=l;}
}

レースをリード!
マウ

3

150文字(空白なし)

void sort(float *v, int n) {
    int l=0;
    float t, *w=v, *z=v+(n-1)/2;

    if (n>0) {
      t=*v; *v=*z; *z=t;
      for(;++w<v+n;)
        if(*w<*v)
        {
          t=v[++l]; v[l]=*w; *w=t;
        }
      t=*v; *v=v[l]; v[l]=t;
      sort(v, l++);
      sort(v+l, n-l);
    }
}

素晴らしい、最初のエントリー!
マウ14年

SSEでお気軽に回答を投稿してください。チャレンジの「ポータブル」ソリューションに興味がありますが、スコアボードに回答を掲載します。
マウ14年

if(*w<*v) { t=v[++l]; v[l]=*w; *w=t; }することができif(*w<*v) t=v[++l], v[l]=*w, *w=t;
ASKASK

3

67 70 69文字

まったく速くありませんが、非常に小さいです。これは、選択ソートとバブルソートアルゴリズムのハイブリッドです。これを実際に読み込もうとしている場合、それ++i-v-nはと同じであることを知っておく必要があります++i != v+n

void sort(float*v,int n){
    while(n--){
        float*i=v-1,t;
        while(++i-v-n)
            *i>v[n]?t=*i,*i=v[n],v[n]=t:0;
    }
}

if(a)b-> a?b:0文字を保存します。
ウゴレン14年

まあ、もちろん条件付きでだけ++i-v-nと同じ++i != v+nです。
wchargin

私はあなたが間違った答えにそのコメントを投稿考える@ugoren
ASKASK

@ASKASK、if(*i>v[n])...->*i>v[n]?...:0
ugoren 14年

それが優位性の仕組みだと確信していますか?
ASKASK 14年

2

123文字(+3改行)

圧縮された標準シェルソート。

d,i,j;float t;
void sort(float*v,int n){
for(d=1<<20;i=d/=2;)for(;i<n;v[j]=t)for(t=v[j=i++];j>=d&&v[j-d]>t;j-=d)v[j]=v[j-d];
}  

PS:クイックソートよりも10倍遅いことを発見しました。このエントリも無視してください。


ギャップの選択はより良い可能性があります。おそらく、これがクイックソートよりもかなり遅い理由です。en.wikipedia.org/wiki/Shellsort#Gap_sequences
FDinoff 14年

ギャップシーケンスが速度にどの程度影響するかを知って驚いた。適切なシーケンスを使用すると、クイックソートに近づきますが、私の経験では遅いままです。
フロリアンF 14年

自分に無理をしないでください。3位です。
ケビン14

2

395文字

マージソート。

void sort(float* v,int n){static float t[16384];float*l,*r,*p,*q,*a=v,*b=v+n/2,
*c=v+n,x;if(n>1){sort(v,n/2);sort(v+n/2,n-n/2);while(a!=b&&b!=c)if(b-a<=c-b&&b-
a<=16384){for(p=t,q=a;q!=b;)*p++=*q++;for(p=t,q=t+(b-a);p!=q&&b!=c;)*a++=(*p<=
*b)?*p++:*b++;while(p!=q)*a++=*p++;}else{for(l=a,r=b,p=t,q=t+16384;l!=b&&r!=c&&
p!=q;)*p++=(*l<=*r)?*l++:*r++;for(q=b,b=r;l!=q;)*--r=*--q;for(q=t;p!=q;)*a++=
*q++;}}}

フォーマット済み。

static float* copy(const float* a, const float* b, float* out)
{   while ( a != b ) *out++ = *a++; return out;
}
static float* copy_backward(const float* a, const float* b, float* out)
{   while ( a != b ) *--out = *--b; return out;
}

static void ip_merge(float* a, float* b, float* c)
{
    /* 64K (the more memory, the better this performs). */
#define BSIZE (1024*64/sizeof(float))
    static float t[BSIZE];

    while ( a != b && b != c )
     {
       int n1 = b - a;
       int n2 = c - b;

       if (n1 <= n2 && n1 <= BSIZE)
        {
          float* p = t, * q = t + n1;
          /* copy [a,b] sequence. */
          copy(a, b, t);
          /* merge. */
          while ( p != q && b != c )
             *a++ = (*p <= *b) ? *p++ : *b++;
          /* copy remaining. */
          a = copy(p, q, a);
        }
       /* backward merge omitted. */
       else
        {
          /* there are slicker ways to do this; all require more support
           * code. */
          float* l = a, * r = b, * p = t, * q = t + BSIZE;
          /* merge until sequence end or buffer end is reached. */
          while ( l != b  && r != c && p != q )
             *p++ = (*l <= *r) ? *l++ : *r++;
          /* compact remaining. */
          copy_backward(l, b, r);
          /* copy buffer. */
          a = copy(t, p, a);
          b = r;
        }
     }
}

void sort(float* v, int n)
{
    if (n > 1)
     {
       int h = n/2;
       sort(v, h); sort(v+h, n-h); ip_merge(v, v+h, v+n);
     }
}

2

331の 326 327 312文字

基数は一度に8ビットをソートします。派手なビットハックを使用して、負の浮動小数点数を取得して正しくソートします(http://stereopsis.com/radix.htmlから盗まれました)。それほどコンパクトではありませんが、非常に高速です(最速のprelimエントリの最大8倍)。私はコードサイズに勝るスピードを望んでいます...

#define I for(i=n-1;i>=0;i--)
#define J for(i=0;i<256;i++)
#define R for(r=0;r<4;r++)
#define F(p,q,k) I p[--c[k][q[i]>>8*k&255]]=q[i]

void sort(float *a, int n) {
  int *A = a,i,r,x,c[4][257],B[1<<20];
  R J c[r][i]=0;
  I {
    x=A[i]^=A[i]>>31|1<<31;
    R c[r][x>>8*r&255]++;
  }
  J R c[r][i+1]+=c[r][i];

  F(B,A,0);
  F(A,B,1);
  F(B,A,2);
  F(A,B,3)^(~B[i]>>31|1<<31);
}

2

511 424文字

インプレース基数ソート

更新:より小さいアレイサイズの挿入ソートに切り替えます(ベンチマークパフォーマンスが4.0倍になります)。

#define H p[(x^(x>>31|1<<31))>>s&255]
#define L(m) for(i=0;i<m;i++)
void R(int*a,int n,int s){if(n<64){float*i,*j,x;for(i=a+1;i<a+n;i++){x=*i;for(
j=i;a<j&&x<j[-1];j--)*j=j[-1];*j=x;}}else{int p[513]={},*q=p+257,z=255,i,j,x,t
;L(n)x=a[i],H++;L(256)p[i+1]+=q[i]=p[i];for(z=255;(i=p[z]-1)>=0;){x=a[i];while
((j=--H)!=i)t=x,x=a[j],a[j]=t;a[i]=x;while(q[z-1]==p[z])z--;}if(s)L(256)R(a+p[
i],q[i]-p[i],s-8);}}void sort(float* v,int n){R(v,n,24);}

フォーマット済み。

/* XXX, BITS is a power of two. */
#define BITS 8
#define BINS (1U << BITS)
#define TINY 64

#define SWAP(type, a, b) \
    do { type t=(a);(a)=(b);(b)=t; } while (0)

static inline unsigned int floatbit_to_sortable_(const unsigned int x)
{   return x ^ ((0 - (x >> 31)) | 0x80000000);
}

static inline unsigned int sortable_to_floatbit_(const unsigned int x)
{   return x ^ (((x >> 31) - 1) | 0x80000000);
}

static void insertsort_(unsigned int* a, unsigned int* last)
{
    unsigned int* i;
    for ( i = a+1; i < last; i++ )
     {
       unsigned int* j, x = *i;
       for ( j = i; a < j && x < *(j-1); j-- )
          *j = *(j-1);
       *j = x;
     }
}

static void radixsort_lower_(unsigned int* a, const unsigned int size,
  const unsigned int shift)
{
    /* @note setup cost can be prohibitive for smaller arrays, switch to
     * something that performs better in these cases. */
    if (size < TINY)
     {
       insertsort_(a, a+size);
       return;
     }

    unsigned int h0[BINS*2+1] = {}, * h1 = h0+BINS+1;
    unsigned int i, next;

    /* generate histogram. */
    for ( i = 0; i < size; i++ )
       h0[(a[i] >> shift) % BINS]++;

    /* unsigned distribution.
     * @note h0[BINS] == h1[-1] == @p size; sentinal for bin advance. */
    for ( i = 0; i < BINS; i++ )
       h0[i+1] += (h1[i] = h0[i]);

    next = BINS-1;
    while ( (i = h0[next]-1) != (unsigned int) -1 )
     {
       unsigned int x = a[i];
       unsigned int j;
       while ( (j = --h0[(x >> shift) % BINS]) != i )
          SWAP(unsigned int, x, a[j]);
       a[i] = x;
       /* advance bins.
        * @note skip full bins (zero sized bins are full by default). */
       while ( h1[(int) next-1] == h0[next] )
          next--;
     }

    /* @note bins are sorted relative to one another at this point but
     * are not sorted internally. recurse on each bin using successive
     * radii as ordering criteria. */
    if (shift != 0)
       for ( i = 0; i < BINS; i++ )
          radixsort_lower_(a + h0[i], h1[i] - h0[i], shift-BITS);
}

void sort(float* v, int n)
{
    unsigned int* a = (unsigned int*) v;
    int i;

    for ( i = 0; i < n; i++ )
       a[i] = floatbit_to_sortable_(a[i]);

    radixsort_lower_(a, n, sizeof(int)*8-BITS);

    for ( i = 0; i < n; i++ )
       a[i] = sortable_to_floatbit_(a[i]);
}

いいね!元の回答にフラグを立ててみてください。
マウ14年

@Mau:ありがとう。ベンチマークコードのエラーに言及したかった。void*in へのキャストqsort(88行目)は、ポインター演算を無効にします。
MojoJojoBojoHojo 14年

1

121の 114 111文字

再帰を使用した、ただの汚いバブルソートです。おそらくあまり効率的ではありません。

void sort(float*v,int n){int i=1;float t;for(;i<n;i++)v[i-1]>(t=v[i])&&(v[i]=v[i-1],v[i-1]=t);n--?sort(v,n):0;}

または、長いバージョン

void sort(float* values, int n) {
  int i=1;  // Start at 1, because we check v[i] vs v[i-1]
  float temp;
  for(; i < n; i++) {
    // If v[i-1] > v[i] is true (!= 0), then swap.
    // Note I am assigning values[i] to temp here. Below I want to use commas
    // so the whole thing fits into one statement, but if you assign temp there you will get sequencing issues (i.e unpredictable swap results)
    values[i - 1] > (temp = values[i]) && (
    // I tried the x=x+y,y=x-y,x=x-y trick, but using a temp
    // turns out to be shorter even if we have to declare the t variable.
      values[i] = values[i - 1], 
      values[i - 1] = temp);
  }

  // If n == 1, we are done. Otherwise, sort the first n - 1 elements recursively. 
  // The 0 is just because the third statement cannot be empty.
  n-- ? sort(values, n) : 0;
}

余談ですが、ここで本当に面白いアルゴリズムを見つけました:rosettacode.org/wiki/Sorting_algorithms/Pancake_sort#C しかし、114に勝るほど圧縮できません:)
CompuChip 14年

あなたのプログラムはある場合には完了せず、他の場合には範囲外に書き出すようです。
マウ

@Mau私はいくつかの入力で手動でテストし、うまくいくように見えましたが、時間の不足のため、私はそれをあまり徹底的にテストしなかったので、どこかに悪い動作があると確信しています。問題が発生したテストケースを投稿してください。
CompuChip 14年

上記で利用可能なテストプログラム:)
マウ14年

うーん、私はそれを実行しようとしました、クリーンアップ部分でいくつかの「munmap_chunk():無効なポインター」エラーが表示されていますが、テストが失敗したことはありません。ただし、1つずれたエラーがあることは正しいです。また、シーケンスの問題がいくつかあるようです(ステートメントのコンマ区切りリストでは、期待どおりの結果が得られません)。私はそれを修正しようとします。
CompuChip 14年

1

221の 193 172文字

ヒープソート-最小ではありませんが、インプレースでO(n * log(n))の動作を保証します。

static void sink(float* a, int i, int n, float t)
{
    float* b = a+i;

    for ( ; (i = i*2+2) <= n; b = a+i )
     {
       i -= (i == n || a[i] < a[i-1]) ? 1 : 0;

       if (t < a[i])
          *b = a[i];
       else
          break;
     }
    *b = t;
}

void sort(float* a, int n)
{
    int i;
    /* make. */
    for ( i = n/2-1; i >= 0; i-- )
       sink(a, i, n, a[i]);
    /* sort. */
    for ( i = n-1; i > 0; i-- )
     {
       float t = a[i]; a[i] = a[0];
       sink(a, 0, i, t);
     }
}

圧縮。

void sort(float* a,int n){
#define F(p,q,r,x,y) for(i=n/p;q>0;){t=a[i];r;for(j=x;(b=a+j,j=j*2+2)<=y&&(j-=(j==y||a[j]<a[j-1]),t<a[j]);*b=a[j]);*b=t;}
float*b,t;int i,j;F(2,i--,,i,n)F(1,--i,a[i]=*a,0,i)
}

空白を差し引くことで、一部の文字を保存できます。また、おそらく必須の関数シグネチャもありますが、数えられるエントリがいくつかあるので、質問者にそれを数えるべきかどうかを明確にするように依頼しました。
ジョナサンヴァンマトレ14年

@ user19425:TEST_COUNT= 3000でテストプログラムを実行すると、少なくとも1つのテストに失敗したようです。
マウ14年

1

154 166文字

OK、こちらはより長いがより速いクイックソートです。

void sort(float*v,int n){while(n>1){float*j=v,*k=v+n-1,t=*j;while(j<k){while(j<k&&*k>=t)k--;*j=*k;while(j<k&&*j<t)j++;*k=*j;}*k++=t;sort(k,v+n-k);n=j-v;}}

以下は、ソートされた入力を生き残るための修正です。また、空白はカウントされないため、フォーマットされます。

void sort(float*v, int n){
    while(n>1){
        float*j=v, *k=j+n/2, t=*k;
        *k = *j;
        k = v+n-1;
        while(j<k){
            while(j<k && *k>=t) k--;
            *j=*k;
            while(j<k && *j<t) j++;
            *k=*j;
        }
        *k++ = t;
        sort(k,v+n-k);
        n = j-v;
    }
}

このバージョンは、ある場合には範囲外に書き込み、他の場合には終了しないようです。
マウ2014年

PS:OK、ソートされたセットでは非常に遅いです。しかし、問題のステートメントは、入力がランダムであると述べています。
フロリアンF 14

値はランダムです。順序については何も言いませんでした:-)しかし、はい、昇順でソートされたすべての値の約10%と降順で別の10%をカバーするチャンクがあります。
マウ14

1
けっこうだ。また、sort()はソートされた入力で動作するはずです。私は...それから、私の提出を更新します
フロリアンF

1

150文字

Shellsort(クヌースギャップ付き)。

void sort(float* v, int n) {
float*p,x;int i,h=0;while(2*(i=h*3+1)<=n)h=i;for(;h>0;h/=3)for(i=h;i<n;i++){x=v[i];for(p=v+i-h;p>=v&&x<*p;p-=h)p[h]=*p;p[h]=x;}
}

フォーマット済み。

static void hsort(float* v, const int h, const int n)
{
    int i;
    for (i = h; i < n; i++) {
        float* p, x = v[i];
        for (p = v + i-h; p >= v && x < *p; p -= h)
            p[h] = *p;
        p[h] = x;
    }
}

void sort(float* v, int n)
{
    int i, h = 0;
    while (2*(i = h*3+1) <= n)
        h = i;
    for (; h > 0; h /= 3)
        hsort(v, h, n);
}

1

C 270(ゴルフ)

#define N 1048576
void sort(float*v,int n)
{
float f[N],g;
int m[N],i,j,k,x;
g=v[0];k=0;
for(i=0;i<n;i++){for(j=0;j<n;j++){if(m[j]==1)continue;if(v[j]<g){g=v[j];k=j;}}f[i]=g;m[k]=1;for(x=0;x<n;x++){if(m[x]==0){g=v[x];k=x;break;}}}
for(i=0;i<n;i++){v[i]=f[i];}
}

説明:連続する各最小数を保管するために、ブランク配列が使用されます。int配列は、番号がまだコピーされていないことを示す0のマスクです。最小値を取得した後、mask = 1は既に使用されている番号をスキップします。次に、配列が元にコピーされます。

ライブラリ関数を使用しないようにコードを変更しました。


0

144

ジョニーのコードを恥知らずに受け取り、小さな最適化を追加して、コードを非常に汚い方法で圧縮しました。短くて速くする必要があります。

コンパイラによっては、sort(q、v + n- ++ q)をsort(++ q、v + nq)に置き換える必要があることに注意してください。

#define w ;while(
void sort(float*v, int n){
    w n>1){
        float *p=v-1, *q=v+n, x=v[n/2], t
        w p<q){
            w *++p<x )
            w *--q>x );
            if( p<q ) t=*p, *p=*q, *q=t;
        }
        sort(q,v+n- ++q);
        n = p-v;
    }
}

実際、コードの作成を開始して最適化しましたが、ジョニーはすでに正しい選択を行っているようです。それで、私は彼のコードに準ずることになりました。私はgotoのトリックを考えていませんでしたが、なしでできました。


0

228文字

基数分類。

void sort(float* v, int n) {
#define A(x,y,z) for(x=y;x<z;x++)
#define B h[(a[i]^(a[i]>>31|1<<31))>>j*8&255]
    int m[1<<20],*a=v,*b=m,*t,i,j;
    A(j,0,4) {
        int h[256] = {};
        A(i,0,n) B++;
        A(i,1,256) h[i] += h[i-1];
        for (i = n-1; i >= 0; i--)
            b[--B] = a[i];
        t = a, a = b, b = t;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.