Qsort比较

时间:2018-08-18 00:39:55

标签: go qsort

我正在将C++代码转换为Go,但在理解此比较功能时遇到困难:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>

using namespace std;

typedef struct SensorIndex
{   double value;
    int    index;
} SensorIndex;

int comp(const void *a, const void* b)
{   SensorIndex* x = (SensorIndex*)a;
    SensorIndex* y = (SensorIndex*)b;

    return abs(y->value) - abs(x->value);
}

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

    SensorIndex *s_tmp;

    s_tmp = (SensorIndex *)malloc(sizeof(SensorIndex)*200);

    double q[200] = {8.48359,8.41851,-2.53585,1.69949,0.00358129,-3.19341,3.29215,2.68201,-0.443549,-0.140532,1.64661,-1.84908,0.643066,1.53472,2.63785,-0.754417,0.431077,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256,-0.123256};

    for( int i=0; i < 200; ++i ) {
        s_tmp[i].value = q[i];
        s_tmp[i].index = i;
    }

    qsort(s_tmp, 200, sizeof(SensorIndex), comp);

    for( int i=0; i<200; i++)
    {   
        cout << s_tmp[i].index << " " << s_tmp[i].value << endl;
    }

}

我希望“ comp”功能可以允许从最高(绝对)值到次要值的排序,但是在我的环境(gcc 32位)中,结果是:

1 8.41851
0 8.48359
2 -2.53585
3 1.69949
11 -1.84908
5 -3.19341
6 3.29215
7 2.68201
10 1.64661
14 2.63785
12 0.643066
13 1.53472
4 0.00358129
9 -0.140532
8 -0.443549
15 -0.754417
16 0.431077
17 -0.123256
18 -0.123256
19 -0.123256
20 -0.123256
...

此外,令我感到奇怪的是,通过使用在线服务执行相同的代码,我得到了不同的值(cpp.sh,C ++ 98):

0 8.48359
1 8.41851
5 -3.19341
6 3.29215
2 -2.53585
7 2.68201
14 2.63785
3 1.69949
10 1.64661
11 -1.84908
13 1.53472
4 0.00358129
8 -0.443549
9 -0.140532
12 0.643066
15 -0.754417
16 0.431077
17 -0.123256
18 -0.123256
19 -0.123256
20 -0.123256
...

有帮助吗?

2 个答案:

答案 0 :(得分:2)

您的比较功能中有一个错误。返回int意味着您失去了绝对差小于1的元素值之间的区别

int comp(const void *a, const void* b)
{
    SensorIndex* x = (SensorIndex*)a;
    SensorIndex* y = (SensorIndex*)b;

    // what about differences between 0.0 and 1.0?
    return abs(y->value) - abs(x->value); 
}

您可以这样解决它:

int comp(const void *a, const void* b)
{   SensorIndex* x = (SensorIndex*)a;
    SensorIndex* y = (SensorIndex*)b;

    if(std::abs(y->value) < std::abs(x->value))
        return -1;

    return 1;
}

一种更现代(更安全)的方法是使用std::vectorstd::sort

// use a vector for dynamic arrays
std::vector<SensorIndex> s_tmp;

for(int i = 0; i < 200; ++i) {
    s_tmp.push_back({q[i], i});
}

// use std::sort
std::sort(std::begin(s_tmp), std::end(s_tmp), [](SensorIndex const& a, SensorIndex const& b){

    return std::abs(b.value) < std::abs(a.value);
});

答案 1 :(得分:2)

此行为是由于使用abs(一种与int一起使用的函数)并将其传递给double参数引起的。 double被隐式转换为int,在比较它们之前会截断小数部分。本质上,这意味着您要获取原始数字,去掉符号,然后去掉小数点右边的所有内容并比较这些值。因此8.123-8.9都被转换为8,并且比较相等。由于输入是反向进行减法运算,因此按大小顺序降序。

您的cpp.sh输出反映了这一点;所有大小在8到9之间的值都将首先出现,然后是3-4s,然后是2-3s,1-2s和少于1个值。

如果您想解决此问题,使其实际上通常按降序排序,则需要一个比较函数,该函数应正确使用the double-friendly fabs function,例如

int comp(const void *a, const void* b)
{   SensorIndex* x = (SensorIndex*)a;
    SensorIndex* y = (SensorIndex*)b;
    double diff = fabs(y->value) - fabs(x->value);
    if (diff < 0.0) return -1;
    return diff > 0;
}

更新:在进一步阅读时,似乎std::abs中的<cmath>double s已有很长时间了,但是std::abs在C ++ 17中,仅double中的<cstdlib>被添加到了abs(整数<cmath>函数所在的位置)中。 And the implementers got this stuff wrong all the time, so different compilers would behave differently at random。无论如何,这里给出的答案都是正确的。如果您尚未包括std::abs,并且您使用的是C ++ 17之前的编译器,则应该仅可以访问基于整数的::abs(或{{ math.h中的1}}),它将在比较之前截断每个值。即使您使用正确的std::absreturning the result of double subtraction as an int would drop fractional components of the difference,也可以使幅度差小于1.0的任何值都相等。更糟糕的是,根据执行的特定比较及其顺序(因为并非所有值都相互比较),这种影响的结果可能会连锁,因为比较顺序更改可能使1.0看起来等于1.6即使2.5彼此比较时,即使1.0可以正确地识别为小于2.5,也将看起来等于r2;从理论上讲,只要每个数字都在其他数字的1.0以内,则比较的结果就好像它们彼此相等(病理情况是,但是肯定会发生较小的错误)。

要点是,弄清楚此代码的 real 意图的唯一方法是弄清楚其最初在其下编译的确切的编译器版本和C ++标准,并在那里进行测试。