qsort在排序2D点时没有给出正确的结果

时间:2016-02-07 10:55:57

标签: c++ sorting

我正在尝试将2d空间中的点w.r.t排序到原点。这是代码:

double distSq(Point p1, Point p2)
{
    return static_cast<double>((p1.x - p2.x)*(p1.x - p2.x) +
      (p1.y - p2.y)*(p1.y - p2.y));
}

int orientation(Point p, Point q, Point r)
{
        double signedArea = double((q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y));

        if (signedArea == 0) return 0;  // colinear
        return (signedArea > 0)? 1: 2; // clock or counterclock wise
}

int compare(const void *vp1, const void *vp2)
{
       Point *p1 = (Point *)vp1;
       Point *p2 = (Point *)vp2;

       // Find orientation
       int o = orientation(origin, *p1, *p2);
       if (o == 0)
         return (distSq(origin, *p2) >= distSq(origin, *p1))? -1 : 1;

       return (o == 2)? -1: 1; 
}

int  main()
{
    int n;
    n = 10;
    Point p[n];
    int i,xx,yy;
    srand(time(NULL));

    p[0].set(1,1);
    p[1].set(1,5);
    p[5].set(5,4);
    p[4].set(4,2);
    p[3].set(2,1);
    p[2].set(3,5);
    p[6].set(2,3);
    p[7].set(4,3);
    p[8].set(3,4);
    p[9].set(3,2);

    cout << "\nThe input points are \n";
    for(i=0;i<n;i++)
        cout << p[i];

    origin=findInterior(p,n);
    cout << "\n\nThe origin is: " << origin << endl;
    qsort(&p[0],n,sizeof(Point),compare);
    cout << "\nThe sorted points are: \n";
    for(i=0;i<n;i++)
        cout << p[i];
    cout << endl;
    return 0;
}

findInterior()函数找到为这些点形成的凸包内部的任何点。 qsort中的问题没有为某些来源(例如(2,2))提供正确的点排序顺序。 例如:

origin(3.33333,3)

sorted order: (4,3)(5,4)(3,5)(3,4)(1,5)(2,3)(1,1)(2,1)(3,2)(4,2) 

这是正确的。但是对于

origin(2,2)

我得到了

(3,2)(4,3)(5,4)(3,4)(2,3)(1,5)(1,1)(2,1)(4,2)(3,5) 

这是不正确的。有许多这样的点,错误的排序顺序来了。 Point类在(x,y)数据类型中具有double。谁能告诉我为什么会出现这个错误?

2 个答案:

答案 0 :(得分:2)

问题出在您的orientation功能中。它不具有传递性:如果a > bb > c,那么a并不总是大于c。这就是问题所在:如果比较函数不是传递函数,则不能有任何有意义的顺序。

非传递性证明:

#include <cassert>

struct Point 
{
    double x, y;
};

Point origin{0,0};

double distSq(Point p1, Point p2)
{ /* Skip */ }

int orientation(Point p, Point q, Point r)
{ /* Skip */ }

int compare(const void *vp1, const void *vp2)
{ /* Skip */ }

int  main()
{
    Point a {5, 0}, b{-5, -5}, c{-5, 5};
    assert(compare(&a, &b) > 0 && "sanity assertion 1");
    assert(compare(&b, &c) > 0 && "sanity assertion 2");
    assert(compare(&a, &c) > 0 && "Transitivity violation");
}
  

断言失败了!

     

表达式:比较(&amp; a,&amp; c)&gt; 0&amp;&amp; &#34;传染性违规&#34;

答案 1 :(得分:0)

我认为你的第一个例子也是错误的。也许您需要compareorientation这样的功能用于其他目的,但是您不需要它们按距离原点的距离对某些点进行排序。

除了compare函数计算距离时,如果点是共线的,并且在orientation中,由于浮点近似,线if (signedArea == 0) ...容易出现舍入误差。

要根据距离给定点的距离对多个点进行排序,您可以使用以下内容:

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <iomanip>

using std::cout;
using std::vector;
using std::pair;

struct Point {
    double x, y;
    void set( double xx, double yy ) {
        x = xx;
        y = yy;
    }
    friend std::ostream &operator<<( std::ostream &o, const Point &p ); 
};

std::ostream &operator<<( std::ostream & o, const Point &p ) {
    return o << '(' << p.x << ',' << p.y << ')';    
}

double distSq( const Point &p1, const Point &p2) {
    return (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y);
}

// sort the vector of points by distance from a point
void sort_points( vector<Point> &vp, Point &o ) {
    // create a temp vector of pairs to store distances (calculated once)
    vector<pair<double,const Point*>> dist_pts;
    for ( auto && i : vp ) {
        dist_pts.push_back(std::make_pair(distSq(o,i),&i));
    }
    // sort pairs using a lambda
    std::sort(dist_pts.begin(),dist_pts.end(),
        []( pair<double,const Point*> &a, pair<double,const Point*> &b) -> bool {
            return a.first < b.first;
        });
    // reorder the original vector
    vector<Point> temp(vp.size());
    for ( auto && i : dist_pts ) {
        temp.push_back(*i.second);
        // show the ordered points and distance, only for testing
        cout << *i.second << ' ' << std::setprecision(6) << sqrt(i.first) << '\n';
    }
}

int  main()
{
    vector<Point> points{ {1,1}, {1,5}, {5,4}, {4,2}, {2,1},
                    {3,5}, {2,3}, {4,3}, {3,4}, {3,2} };

    Point origin{3.3333,3};
    cout << "Origin: " << origin << '\n';
    sort_points(points,origin);

    origin.set(2,2);
    cout << "\nOrigin: " << origin << '\n';
    sort_points(points,origin);

}

排序点的顺序为:

Origin: (3.3333,3)
(4,3) 0.6667
(3,4) 1.05408
(3,2) 1.05408
(4,2) 1.20187
(2,3) 1.3333
(5,4) 1.94368
(3,5) 2.02758
(2,1) 2.40368
(1,1) 3.07316
(1,5) 3.07316

Origin: (2,2)
(2,1) 1
(2,3) 1
(3,2) 1
(1,1) 1.41421
(4,2) 2
(4,3) 2.23607
(3,4) 2.23607
(1,5) 3.16228
(3,5) 3.16228
(5,4) 3.60555