std :: stable_sort根本不稳定?

时间:2015-08-10 22:11:32

标签: c++ algorithm sorting c++11

std::stable_sort数据类型上使用Point时,我遇到了一个奇怪的错误。 Point值的默认排序首先由x坐标确定,然后由y坐标确定以打破关系。但是,我使用std::stable_sort根据相对于外部点的斜率对点进行排序。我一直发现std::stable_sort没有保留相对于外部点具有相同斜率的点的相对排序。

这是正确执行并产生预期输出的代码。

std::vector<Point> points;
/* 
Work is done here to fill the vector with Points.
These Points form several horizontal lines. 
*/
std::sort(points.begin(), points.end()); //Sorts Points based on their coordinates
for (const Point& elem : points)  //Displays sorted vector
    std::cout << elem << ' ';

Output:
(1888, 7657) (2682, 14118) (4750, 4652) (5067, 14118) (5766, 4652) (7453, 14118) 
(7599, 7657) (7821, 14118) (8934, 7996) (9972, 4652) (10375, 12711) (10411, 7996) 
(12772, 7657) (13291, 7996) (13832, 7657) (14226, 12711) (16307, 4652) (18177, 12711) 
(20385, 12711) (20547, 7996) 
//The Points are sorted in order of increasing x-coordinate

但是当我使用std::stable_sort根据相对于第一个元素的所有元素的斜率对矢量进行排序时,即使在具有相同斜率的点之间,矢量也不再按照增加x坐标的顺序排序。

const Point& P0 = points[0];
auto compLambda = [&](const Point& a, const Point& b)
    {return P0.slopeCompare(a,b) != 1;}
std::stable_sort(points.begin() + 1, points.end(), compLambda);
for (const Point& elem : points)
    std::cout << elem << ' ';

Output:
(1888, 7657) //First point remains unmoved, as expected
(4750, 4652) (5766, 4652) (9972, 4652) (16307, 4652)

/*These points all form a horizontal line with the first point.*/
(13832, 7657) (12772, 7657) (7599, 7657) 
/*However, they appear to be sorted in order of DECREASING x-coordinates!??!?*/

(20547, 7996) (13291, 7996) (10411, 7996) (8934, 7996) (20385, 12711) (18177, 12711) 
(14226, 12711) (10375, 12711) (7821, 14118) (7453, 14118) (5067, 14118) (2682, 14118)

一般来说,std::stable_sort似乎始终如一地神奇地颠倒了具有相同斜率的元素的相对排序。除此之外,上面提到的方法似乎工作正常。我已经发布了以下Point类的方法(operator<<除外),但我不知道其中任何一个错误应该导致std::stable_sort变得不稳定。< / p>

这是Point类的压缩版本。

#include <limits>

class Point{
private:
    int x, y;
public:
    ///Default constructor for Point[] arrays
    Point(void) = default;   

    ///Regular constructor
    Point(int x, int y): x(x), y(y) {}

    ///Default comparison operators
    inline bool operator<(const Point& other){
        return x < other.x || x == other.x && y < other.y;
    }
    inline bool operator>(const Point& other){
        return x > other.x || x == other.x && y > other.y;
    }
    inline bool operator==(const Point& other){
        return x == other.x && y == other.y;
    }

    ///Returns the slope between this point and another. Note that vertical   
    ///lines have a slope of INF and the method returns -INF if the argument 
    ///is the same as the instance. Also, horizontal lines are treated 
    ///specially to prevent evaluation of -0.0
    inline double slopeTo(const Point& other){
        if (x == other.x)
            return y == other.y ? -HUGE_VAL : HUGE_VAL;
        else if (y == other.y)
            return 0;
        else
            return static_cast<double>(y - other.y)/(x - other.x);
    }

    ///Slope comparator
    inline int slopeCompare(const Point& a, const Point& b){
        if (slopeTo(a) > slopeTo(b))
            return 1;
        else if (slopeTo(a) < slopeTo(b))
            return -1;
        else
            return 0;
    }
};

3 个答案:

答案 0 :(得分:4)

由于双倍的舍入,slopeTo本质上是不稳定的。因此,斜率比较是不稳定的,因此使用它最好是不稳定的,并且可能比不稳定更糟糕。

通过交叉乘法比较斜率而不是分割,可以获得更好的结果。但对于仍然不完美的大值。

编辑:初看起来我错过了更严重的错误:

auto compLambda = [&](const Point& a, const Point& b)
    {return P0.slopeCompare(a,b) != 1;}

当需要返回false时,对于== b返回true。

答案 1 :(得分:2)

您的比较运算符对std::stable_sort无效,因为它未实现strict weak ordering

inline bool operator<(const Point& other){
    return x < other.x || x == other.x && y == other.y;
}

有了这个逻辑,如果2分ab相同,a<b将返回true b<a也将返回true

与严格弱排序的有效比较将是

inline bool operator<(const Point& other){
    return x < other.x || (x == other.x && y < other.y);
}

答案 2 :(得分:2)

operator <

return x < other.x || x == other.x && y == other.y;

你需要

return x < other.x || x == other.x && y < other.y;

就个人而言,我也会加上一些括号。您还可以通过将operator >(a, b)定义为!(b < a)来保存一些输入内容。