基数排序循环错误

Posted

技术标签:

【中文标题】基数排序循环错误【英文标题】:Radix Sort Looping Bug 【发布时间】:2013-10-19 20:58:13 【问题描述】:

我正在尝试在 C 中为整数实现基数排序,但遇到了一个似乎无法修复的循环错误。这是代码和输出。我不知道确切的错误部分,所以请原谅帖子的长度。

谁能指出我的错误?

#include <stdio.h>
#define BINS 16
#define GROUP 4

int main(int argc, const char *argv[])

    int mask = 0xf;
    int i, j;
    int list[] = 0x65c6, 0xbeb, 0x96ba, 0x9a7d;
    int buffer[GROUP];
    int *temp, *src_ptr, *dest_ptr;
    int cnt[BINS];
    int map[BINS];
    map[0] = 0;

    //init pointers to the list of unsorted numbers and temp buffer
    src_ptr = list;
    dest_ptr = buffer;

    //print unsorted list
    putchar('\n');
    printf("unsorted list: \n");
    for(i = 0; i < GROUP; i++) 
        printf("int: %d hex: 0x%x ", src_ptr[i], src_ptr[i]);
    putchar('\n');

    j = 0;
    while(j < GROUP)
    
        //initalize the count
        for(i = 0; i < BINS; i++)
            cnt[i] = 0;
        
        //count significant digits. shifting i * group # of times
        for(i = 0; i < GROUP; i++)
            cnt[(src_ptr[i] >> i*GROUP) & mask]++;

        //initalize the map
        map[0] = 0;
        for(i = 0; i < BINS; i++)
            map[i] = 0;

        //compute the map
        for(i = 1; i < BINS; i++)
        
            map[i] = (map[i - 1] + cnt[i - 1]);
        

        //shift the elements in buffer[] and list[] via their pointers. 
        //shifting i * group # of times
        for(i = 0; i < GROUP; i++)
        
            dest_ptr[map[(src_ptr[i] >> i*GROUP) & mask]++] = src_ptr[i];
        

        //perform a swap of list[] and buffer[] via their pointers
        temp = src_ptr;
        src_ptr = dest_ptr;
        dest_ptr = src_ptr;
    
        j++;
    

    //print list for reference
    putchar('\n');
    printf("sorted list: \n");
    for(i = 0; i < GROUP; i++) 
        printf("int: %d hex: 0x%x ", src_ptr[i], src_ptr[i]);
    putchar('\n');
    
    //print buffer for reference
    putchar('\n');
    printf("sorted buffer: \n");
    for(i = 0; i < GROUP; i++) 
        printf("int: %d hex: 0x%x ", dest_ptr[i], dest_ptr[i]);
    putchar('\n');

    return 0;
    

输出:

未排序的原始列表: 整数:26054 十六进制:0x65c6 整数:3051 十六进制:0xbeb 整数:38586 十六进制:0x96ba 整数:39549 十六进制:0x9a7d

排序列表: 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb

排序缓冲区: 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb 整数:3051 十六进制:0xbeb

【问题讨论】:

你的交换代码:temp = src_ptr; src_ptr = dest_ptr; dest_ptr = src_ptr; 应该引用temp 两次(我的编译器告诉我你做错了,因为它说“错误:变量'temp' set but not used [-Werror=未使用但设置变量]")。你需要让你的编译器产生类似的警告,然后注意它们。交换代码当然应该是:temp = src_ptr; src_ptr = dest_ptr; dest_ptr = temp;。这是一个必要的改变(假设那里需要交换);这还不够。 【参考方案1】:

代码有两个问题:

    你的交换代码:temp = src_ptr; src_ptr = dest_ptr; dest_ptr = src_ptr; 应该引用temp 两次(我的编译器告诉我你做错了,因为它说“error: variable ‘temp’ set but not used [-Werror=unused-but-set-variable]”)。你需要让你的编译器产生类似的警告,然后注意它们。交换代码当然应该是:temp = src_ptr; src_ptr = dest_ptr; dest_ptr = temp;。这是必要的改变;这还不够。

    我希望我的代码能够在以下条件下干净地编译:

    gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration -Werror  radixsort.c -o radixsort
    

    您在换班时没有正确使用j。你有:

    cnt[(src_ptr[i] >> i*GROUP) & mask]++;
    dest_ptr[map[(src_ptr[i] >> i*GROUP) & mask]++] = src_ptr[i];
    

    你需要:

    cnt[(src_ptr[i] >> j*GROUP) & mask]++;
    dest_ptr[map[(src_ptr[i] >> j*GROUP) & mask]++] = src_ptr[i];
    

此代码似乎排序正确:

#include <stdio.h>

enum  BINS  = 16  ;
enum  GROUP = 4   ;
enum  MASK  = 0xF ;

static void dump_array(char const *tag, size_t n, int a[n])

    printf("%s:\n", tag);
    for (size_t i = 0; i < n; i++)
        printf("int: %5d hex: 0x%.4X\n", a[i], a[i]);


int main(void)

    int i, j;
    int list[] = 0x65C6, 0x0BEB, 0x96BA, 0x9A7D;
    int buffer[GROUP];
    int *temp, *src_ptr, *dest_ptr;
    int cnt[BINS];
    int map[BINS];
    map[0] = 0;

    // init pointers to the list of unsorted numbers and temp buffer
    src_ptr = list;
    dest_ptr = buffer;

    // print unsorted list
    dump_array("unsorted list", GROUP, src_ptr);

    j = 0;
    while (j < GROUP)
    
        // initalize the count
        for (i = 0; i < BINS; i++)
            cnt[i] = 0;

        // count significant digits. shifting i * group # of times
        for (i = 0; i < GROUP; i++)
            cnt[(src_ptr[i] >> j*GROUP) & MASK]++;

        // initalize the map
        map[0] = 0;
        for (i = 0; i < BINS; i++)
            map[i] = 0;

        // compute the map
        for (i = 1; i < BINS; i++)
        
            map[i] = (map[i - 1] + cnt[i - 1]);
        

        // shift the elements in buffer[] and list[] via their pointers.
        // shifting i * group # of times
        for (i = 0; i < GROUP; i++)
        
            dest_ptr[map[(src_ptr[i] >> j*GROUP) & MASK]++] = src_ptr[i];
        

        // perform a swap of list[] and buffer[] via their pointers
        temp = src_ptr;
        src_ptr = dest_ptr;
        dest_ptr = temp;
        j++;
    

    // print list for reference
    dump_array("sorted list", GROUP, src_ptr);

    // print buffer for reference
    dump_array("sorted buffer", GROUP, dest_ptr);

    return 0;

样本输出:

unsorted list:
int: 26054 hex: 0x65C6
int:  3051 hex: 0x0BEB
int: 38586 hex: 0x96BA
int: 39549 hex: 0x9A7D
sorted list:
int:  3051 hex: 0x0BEB
int: 26054 hex: 0x65C6
int: 38586 hex: 0x96BA
int: 39549 hex: 0x9A7D
sorted buffer:
int: 26054 hex: 0x65C6
int: 38586 hex: 0x96BA
int: 39549 hex: 0x9A7D
int:  3051 hex: 0x0BEB

通用代码

上面的代码使用GROUP 用于两个不同的目的。一个是要排序(和打印)的列表的长度。一个是用于进行基数排序的数字组数。下面的代码被概括为将列表大小与基数组分开。它还清理了一些原始代码中未修复的cmets等。

#include <stdio.h>

enum  BINS  = 16  ;
enum  GROUP = 4   ;
enum  MASK  = 0xF ;

static void dump_array(char const *tag, size_t n, int a[n])

    printf("%s:\n", tag);
    for (size_t i = 0; i < n; i++)
        printf("int: %5d hex: 0x%.4X\n", a[i], a[i]);


int main(void)

    int list[] = 0x65C6, 0x0BEB, 0x96BA, 0x9A7D, 0x2917, 0x8A2C, 0xDEAD, 0xBEEF, 0xFACE ;
    enum  LIST_SIZE = sizeof(list) / sizeof(list[0]) ;
    int buffer[LIST_SIZE];
    int cnt[BINS];
    int map[BINS];

    // init pointers to the list of unsorted numbers and temp buffer
    int *src_ptr = list;
    int *dst_ptr = buffer;

    // print unsorted list
    dump_array("unsorted list", LIST_SIZE, src_ptr);

    for (int j = 0; j < GROUP; j++)
    
        // initalize the count
        for (int i = 0; i < BINS; i++)
            cnt[i] = 0;

        // count significant digits. shifting j * group # of times
        for (int i = 0; i < LIST_SIZE; i++)
            cnt[(src_ptr[i] >> j*GROUP) & MASK]++;

        // initalize the map
        for (int i = 0; i < BINS; i++)
            map[i] = 0;

        // compute the map
        for (int i = 1; i < BINS; i++)
            map[i] = (map[i - 1] + cnt[i - 1]);

        // shift the elements in buffer[] and list[] via their pointers.
        // shifting j * group # of times
        for (int i = 0; i < LIST_SIZE; i++)
            dst_ptr[map[(src_ptr[i] >> j*GROUP) & MASK]++] = src_ptr[i];

        // perform a swap of list[] and buffer[] via their pointers
        int *tmp_ptr = src_ptr;
        src_ptr = dst_ptr;
        dst_ptr = tmp_ptr;
    

    // print list for reference
    dump_array("sorted list", LIST_SIZE, src_ptr);

    // print buffer for reference
    dump_array("sorted buffer", LIST_SIZE, dst_ptr);

    return 0;

此代码现在假定整数值都在 0x0000..0xFFFF 范围内(每个 4 位的 4 个 nybbles,或 16 位数字)。

样本输出:

unsorted list:
int: 26054 hex: 0x65C6
int:  3051 hex: 0x0BEB
int: 38586 hex: 0x96BA
int: 39549 hex: 0x9A7D
int: 10519 hex: 0x2917
int: 35372 hex: 0x8A2C
int: 57005 hex: 0xDEAD
int: 48879 hex: 0xBEEF
int: 64206 hex: 0xFACE
sorted list:
int:  3051 hex: 0x0BEB
int: 10519 hex: 0x2917
int: 26054 hex: 0x65C6
int: 35372 hex: 0x8A2C
int: 38586 hex: 0x96BA
int: 39549 hex: 0x9A7D
int: 48879 hex: 0xBEEF
int: 57005 hex: 0xDEAD
int: 64206 hex: 0xFACE
sorted buffer:
int: 26054 hex: 0x65C6
int: 38586 hex: 0x96BA
int: 10519 hex: 0x2917
int: 35372 hex: 0x8A2C
int: 39549 hex: 0x9A7D
int: 64206 hex: 0xFACE
int:  3051 hex: 0x0BEB
int: 57005 hex: 0xDEAD
int: 48879 hex: 0xBEEF

【讨论】:

喂!落后你1分钟! Curse you Red Barron FGITW 再次来袭:D(那是西方最快的枪)。 谢谢。我在我的机器上试过这个版本,它似乎没有正确排序。但是如果我将while循环的终止条件更改为8,即while (j &lt; 8)。它工作正常。有什么建议么?我没有使用dump_array 函数。 不知道。为什么不使用与dump_array() 函数等效的函数?你喜欢多次编写相同的代码吗?这些函数在调试代码时非常有用。如果您尝试对列表中的 4 个以上的项目进行排序,或者您尝试对 0x0000..0xFFFF 范围之外的值进行排序,那么您的原始代码将无法很好地适应。查看我的修订版。 我并不是说我反对使用dump_array 函数,我只是根据您的代码进行了一些快速更改。我同意dump_array 更好。但就我的循环问题而言,即while (j &lt; 8)。我意识到原始代码应该是(while j &lt;= 4),因为我认为它指的是被排序的整数中的位数。再次感谢您的帮助,代码现在可以正常工作了。

以上是关于基数排序循环错误的主要内容,如果未能解决你的问题,请参考以下文章

这个基数排序代码中的最后一个“for”循环是做啥的?

计数排序和基数排序的实现

数据结构-基数排序

字符串算法—字符串排序(下篇)

排序算法 - 基数排序

为啥当我实现以 2^20 为底的基数排序以对大小为 500 万的数组进行排序时,该程序会陷入无限循环?