【问题标题】:generic Insertion sort can't sort floating point array通用插入排序无法对浮点数组进行排序
【发布时间】:2020-02-07 01:31:40
【问题描述】:

我编写了一个通用版本的插入排序算法。它适用于 int 和 char,但它不能对浮点数进行排序。我想不通。它是否必须与内存中浮点数的表示或其他什么有关?

这是我的代码:

void insertion_sort(void *array, int elemSize, int arrSize){
    for(int i = 0;i < arrSize;i++){
        for(int j = 1;j < arrSize;j++){
            void *elem1 = (char *) array + ((j-1) * elemSize); 
            void *elem2 = (char *) array + (j * elemSize);
            if(memcmp(elem2,elem1,elemSize) < 0){
                void *temp = malloc(sizeof(elemSize));
                memcpy(temp,elem1,elemSize);
                memcpy(elem1,elem2,elemSize);
                memcpy(elem2,temp,elemSize);
                free(temp);
            }
        }
    }
}
Sample output of insertion sort:
char:-)
Unsorted :      e d a c b
Sorted   :      a b c d e

int:-)
Unsorted :      4 5 2 3 1
Sorted   :      1 2 3 4 5

float:-(
Unsorted :      3.3 1.2 44.5 5.1 2.8
Sorted   :      44.5 2.8 3.3 5.1 1.2

编辑: 此代码也不适用于 int 或 char。看看我在这里写的另一个版本[链接]:https://bitbucket.org/Kessinger/c-generics/src/master/generic_insertion_sort.c

【问题讨论】:

  • 考虑在 for 循环之外预分配 temp,如果你有一些有效的大小,同样删除。还需要一个 int (*compareFcn)(const void*, const void*) 形式的自定义比较器来对 any 数据类型进行排序。
  • memcmp 一次比较一个字节,并在第一个差异处停止。因此,此代码仅适用于 big-endian system 上的 int 类型。对于int 测试,尝试 400,500,200,300,100 以查看您的系统是否为大端。大多数系统都不是。
  • @user3386109 我的系统是 little endian,所以我可以对 int 进行排序
  • 你试过未排序的值 400,500,200,300,100 吗?
  • 查看标准C qsort() 函数。它接受一个比较器函数指针参数,以便您可以适当地比较值(包括按升序或降序排序)。您需要做同样的事情才能从您的通用排序中获得合理的结果。比较器函数参数不是偶然的,也不是因为人们想让生活变得困难的标准;它可以使通用 qsort() 正常工作。

标签: c


【解决方案1】:

虽然 OP 的 answer 是朝着正确方向迈出的一步,但仍有一些问题需要解决。

例如,这一行:

void *temp = malloc(sizeof(elemSize));
//                  ^^^^^^^  

如果数组的元素不是ints(elemSize的类型),将分配错误的字节数。

已发布的sn-p中的条件

if (memcmp(elem2, elem1, elemSize) < 0) {

comment 部分所述,错误已被替换为调用(未显示)比较函数。为了符合qsort的要求,如果我们要对数组进行升序排序,如果第一个参数小于第二个,该函数应该返回一个负整数值,如果第一个参数大于一个正整数值如果参数相等,则比第二个和零。一个简单的实现,例如int 的值可能如下:

int cmp_int(const void *lhs, const void *rhs)
{
    int a = *(int *)lhs;
    int b = *(int *)rhs;
    if (b < a) {
        return 1;
    }
    else if (a < b) {
        return -1;
    }
    else {
        return 0;
    }
}

请注意,这个自定义点不仅可以用于泛化不同类型的排序功能,还可以用于排序方向(升序或降序)。

一个小细节(不是错误,只是效率低下)是内部循环执行了太多次(总是从 1 到 arrSize),对于 O(n2) 算法,但它们仍然是所需时间的两倍。该函数的修改版本如下所示

typedef int (cmp_fn) (void const *, void const *);

void insertion_sort(size_t array_size, void *array,
                    size_t elem_size,
                    cmp_fn cmp)
{
    void *buf = malloc(elem_size);
    //                 ^^^^^^^^^
    for(size_t i = 1; i < array_size; ++i)
    {
        for(size_t j = i; j > 0;)
        { //           ^^^^^^^^^  
            void *b = (char *) array + (j * elem_size);
            void *a = (char *) array + (--j * elem_size);
            //                          ^^^
            if(cmp(a, b) == 1)
            {
                memcpy(buf, a, elem_size);
                memcpy(a, b, elem_size);
                memcpy(b, buf, elem_size);
            }
        }
    }
    free(buf);
}

编辑

添加到问题的链接也导致了这种尝试编写通用打印函数(cmets mine):

void printArr(void *arr, int size, int elemSize) {
    for(int i = 0; i < size; i++) {
        void *elem = (char *) arr + (i * elemSize);
        if(elemSize == sizeof(int)) {
        // ^^^^^^^^^^^^^^^^^^^^^^^            
            printf("%d ",*(int *)elem);
        } else if (elemSize == sizeof(double)) {
            //     ^^^^^^^^^^^^^^^^^^^^^^^^^      Note the type 
            printf("%1.1f ",*(float *)arr + (i *elemSize));
            //          ^     ^^^^^^^             Ops...
        } else if (elemSize == sizeof(char)) {
            printf("%c ",*(char *)elem);
        }
    }
    printf("\n");
}

除了类型不匹配之外,使用elemSize 来确定数组的类型不是一个好主意,至少不是很便携。例如,虽然sizeof(int) 等于sizeof(char) 的架构可能并不常见,但如今sizeof(int) 等于sizeof(float) 并且几乎可以肯定sizeof(float) 小于sizeof(double) 是很常见的。这也无法区分intunsigned int,但会使用相同的格式说明符。

作为练习,实现类似 sort 的通用打印函数可能会很有趣,将指针传递给负责以正确方式打印元素的函数。

值得注意的是,这种实现泛化的方法要求程序员非常小心,特别是在涉及类型的匹配方面,以避免未定义的行为。

【讨论】:

    【解决方案2】:

    编写此函数的更好方法是为 int、char 和 float 提供一个比较器函数,如下所示:

    insertion_sort (void *arr, int elemSize, int arrSize, int (*cmp)(const void *,const void *))

    然后将 if 条件更改为:

    if (cmp(elem2,elem1))

    如果 elem2 则假设 cmp 返回 1。

    temp 变量也应该在 for 循环外部声明并在最后释放。

    Insertion_sort经过以上修改后应该是这样的:

    void insertion_sort(void *array, int elemSize, int arrSize, int (*cmp)(const void *,const void *)){
        void *temp = malloc(sizeof(elemSize));
        for(int i = 0;i < arrSize;i++){
            for(int j = 1;j < arrSize;j++){
                void *elem1 = (char *) array + ((j-1) * elemSize); 
                void *elem2 = (char *) array + (j * elemSize);
                if(cmp(elem2,elem1)){
    
                    memcpy(temp,elem1,elemSize);
                    memcpy(elem1,elem2,elemSize);
                    memcpy(elem2,temp,elemSize);
                }
            }
        }
        free(temp);
    }
    

    【讨论】:

    • 比较器功能版本更好,但这并不能回答问题,因为它没有提到为什么原始代码不起作用。请改为编辑问题以仅包含您遇到问题的实际代码
    猜你喜欢
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    相关资源
    最近更新 更多