2010-11-17 74 views
5

我有一个小问题,找不到满意的解决方案。 有一个字节数组,我需要这些字节按高7位排序,而 保留低位的顺序。快速排序的字节数组

所以最初它看起来是这样的:

// sort buf[N] to tmp[N] 
uint offs[128+1]; uint c,i,s; 
for(i=0; i<128; i++) offs[i]=0; 
for(i=0; i<l; i++) offs[buf[i]>>1]++; 
for(i=0,s=0; i<128; i++) c=offs[i], offs[i]=s, s+=c; offs[i]=s; 

byte* tmp = new byte[N]; 
for(i=0; i<N; i++) c=buf[i], tmp[offs[c>>1]++]=c; // sort 

但这些块是足够大(目前8M),我想用多线程, 和每个线程的额外8M是明显的。

所以我试图用一些简单的基数排序:

void radix(byte* buf, uint h, uint l, uint mask) { 
    uint p = (h+l)>>1, q = h; 
    uint i = offs[h], j = offs[l]-1; h = offs[p]; 
    if((i<h) && (j>=h)) { 
    byte c = buf[i], d = buf[j]; 
    while((i<h) && (j>=h)) { 
     while((c&mask)==0) c = buf[++i]; // find value with bit 1 
     while((d&mask)!=0) d = buf[--j]; // find value with bit 0 
     buf[i]=d; buf[j]=c; // swap 1-0 -> 0-1 
     c = buf[++i]; d = buf[--j]; 
    } 
    if(mask>=4) { 
     radix(buf, q,p, mask>>1); 
     radix(buf, p,l, mask>>1); 
    } 
    } 
} 

但它改变了这些低位的顺序,它变得不可用。

其实一些简单的方法,像bubblesort,只是做我想要的,但它们慢得多,速度也是一个问题。

所以目前我排序经由临时缓冲区更小的块,然后使用 索引表来访问,以便部分地排序的块:

struct tmpsort { 

    enum{ blocksize = (1<<16)-1 }; 

    unsigned short ofs[(max_quants+blocksize-1)/blocksize][probN]; 

    tmpsort(byte* buf, uint f_len) { 
    uint i,j,k; 
    uint freq[2*probN]; // prob freqs 
    byte tmp[blocksize+1]; 

    for(k=0,j=0; k<f_len; k+=blocksize,j++) { 
     uint l = Min(k+blocksize,f_len)-k; 
     byte* p = &buf[k]; 

     // compute offsets of sorted chunks 
     for(i=0; i<2*probN; i++) freq[i]=0; 
     for(i=0; i<l; i++) freq[p[i]]++; 
     for(i=0; i<probN; i++) freq[i+1]=freq[2*i+0]+freq[2*i+1]; // 1=0+1, 2=2+3, 3=4+5 
     freq[0] = 0; 
     for(i=0; i<probN; i++) freq[i+1]+=freq[i]; 
     for(i=0; i<probN; i++) ofs[j][i]=freq[i+1]; 

     // sort the block via tmp 
     for(i=0; i<l; i++) { byte c=p[i]; tmp[freq[c>>1]++]=c; } 
     for(i=0; i<l; i++) p[i]=tmp[i]; 
    } 
    } 

}; 

[...] 

tmpsort ts(buf, f_len); 
for(i=0; i<probN; i++) { 
    for(k=0,j=0; k<f_len; k+=ts.blocksize,j++) { 
    uint x = i>0 ? ts.ofs[j][i-1] : 0; 
    for(; x<ts.ofs[j][i]; x++) putc(buf[k+x],g); 
    } 
} 

但是TMP []和OFS []阵列使用太多堆栈空间,它的 不是一个完整的排序,所以我一直想知道是否有一些整洁的解决方案。

数据和我实现的一个样本在这里可供选择: http://nishi.dreamhosters.com/u/tmpsort_v0.rar

回答

0

有了额外的64kB,你可以(如你所注意到的)以压缩的形式存储一个512kbit的块(减去一些固定数量的索引数据)(只存储每个键的最低位)。他们到他们压缩排序的形式,压缩他们,当你走在整个数组的开始。

现在将压缩表单合并成一个大的压缩表单(释放7M后很容易)。然后解压回到排序后的数组。

这是O(N),虽然常数看起来相当大,但涉及一些非平凡位操作的3遍。

+0

谢谢,我真的错过了这个方法,可能值得尝试。 – Shelwien 2010-11-17 21:56:40

1

为什么不使用任何标准就地,稳定sorting algorithm,例如Insertion Sort,并实现适当的比较器功能?

+0

带两个缓冲区的解决方案需要N次读取和N次写入。 我在这里需要的东西很快,标准排序实现不适用于字节排序。 – Shelwien 2010-11-17 14:05:10

0

可以将quicksort作为稳定的排序来实现。就big-O而言,它并不比插入排序更好,但实际上它会更好地执行批次。如果您对大小为6或8的树叶进行硬编码排序网络,我认为这是您将获得稳定的就地排序的最佳性能。

其实......据说有一种就地稳定的合并排序。就理想的理论特征而言,它是所有在同一时间的原地排列,真正的稳定的圣杯。但我怀疑这是一个巨大的痛苦实施,并有相当大的恒定条件去与那个大O。

+0

我认为这里只有128个不同的钥匙非常重要。我还考虑在这里通过xy = reverse(reverse(y)+ reverse(x))实现一个按位合并器,这里 (0(10)1 - > 0011),但它看起来比那个单线环路慢得多。 。 – Shelwien 2010-11-17 16:09:39

+0

顺便说一句,它需要15.610s处理一个100M的文件使用第一个版本有额外的缓冲和17.594s使用上述 – Shelwien 2010-11-17 16:17:18

+0

是“tmpsort”但你要不断地为那些低位仍有大量的信息;保持他们不会自由。如果你不介意使用一个单独的输出缓冲区,我有一个快速的算法,我会发布作为另一个答案。 – 2010-11-17 16:19:16

1

这可以用在超过O一点相对简单的代码使用一个版本的基数排序的执行在各7个重要比特的一个稳定的排序,从至少显著到最显著(N log n)的时间来完成。这种技术相对于稳定的就地合并排序的优点是,如果你自己编写代码,代码就简单得多。

下面是由一个规定的位来执行就地稳定排序的功能。这里,递归编写使用O(LG N)的堆栈空间的简单(该堆栈空间的使用可以,如果你想使用一个for循环来组织分而治之的办法消除):

// sort array x from i to j by bit b 
sort(x, i, j, b) { 
    if (i >= j - 1) return; 
    mid = (i + j)/2; 
    sort(x, i, mid, b); 
    sort(x, mid, j, b); 
    first1 = -1; 
    last0 = -1; 
    for (k = i; k < j; k++) { 
    if (first1 < 0 && isSet(x[k], b)) first1 = k; 
    if (!isSet(x[k], b)) last0 = k; 
    } 
    if (last0 < first1) return; 

    // the sequence of bit b generally looks something like 0000011100000111111 
    // so we reverse from the first 1 to the last 0 
    reverse(x, first1, last0afterfirst1); 
    newlast0 = first1; 
    while (!isSet(x[++newlast0], b)); 
    newlast0--; 

    // the elements in the range first1..last0 are in the wrong order, so reverse 
    reverse(x, first1, newlast0); 
    reverse(x, newlast0 + 1, last0); 
} 

功能isSet测试是否设置了一位,reverse执行就地阵列反转。上述排序子程序被调用的每个位(如在基数排序)如下:

sort(x) { 
    for (b = 1; b < 8; b++) { 
    sort(x, 0, n, b); 
    } 
} 

总运行时间为“O(7 * N log n)的”。如果该算法得到推广,则额外因子7可能是可变的。

+0

谢谢,但我意识到这一点,正如你可以从我的评论中看到的那样,你的实现看起来比我想象的还要慢:)。在这种情况下,N * log(N)也很糟糕,因为log2(8M)是23.实际上,通过查找所有匹配键,7 * 23 * 8M甚至比128 * 8M更糟。 – Shelwien 2010-11-17 21:52:09

+0

噢,好的,我认为你唯一的抱怨是它不是一个稳定的排序。 – jonderry 2010-11-17 22:16:47