qsort 在对 2D 点进行排序时未给出正确的结果

qsort not giving proper result while sorting 2D points

本文关键字:结果 2D 在对 排序 qsort      更新时间:2023-10-16

我正在尝试将 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 << "nnThe 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。谁能告诉我为什么会出现此错误?

问题出在您的orientation函数中。它不是传递的:如果a > b并且b > ca并不总是大于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");
}

断言失败!

表达式:比较(&a,&c)>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