我正在尝试将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
。谁能告诉我为什么会出现这个错误?
答案 0 :(得分:2)
问题出在您的orientation
功能中。它不具有传递性:如果a > b
和b > 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)
我认为你的第一个例子也是错误的。也许您需要compare
或orientation
这样的功能用于其他目的,但是您不需要它们按距离原点的距离对某些点进行排序。
除了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