【问题标题】:C++ std::sort with custom functionC++ std::sort 与自定义函数
【发布时间】:2016-01-03 09:22:25
【问题描述】:

我有一个结构点:

typedef struct Point
{
    double x;
    double y;

    Point operator-(const Point &b)
    {
        Point pt;
        pt.x = this->x - b.x;
        pt.y = this->y - b.y;
        return pt;
    }

    friend std::ostream& operator<<(std::ostream& os, const Point& pt)
    {
        return os << "X: " << pt.x << "\t\t\tY: " <<pt.y << std::endl; 
    }

    friend std::istream& operator>>(std::istream& is, Point& pt)
    {
        return is >> pt.x >> pt.y;
    }
}Point;

我正在尝试从vector&lt;Point&gt; 中找到最小元素,然后根据与该最小点的角度进行排序。

下面是相应的代码:

bool lex_yx(const Point &a, const Point &b)
{
    if(a.y < b.y)
        return true;

    if (a.y == b.y)
        return (a.x < b.x);

    return false;
}

bool CalcAngRad_Compare (const Point &p_min, Point &a, Point &b)
{
    Point subPt_1, subPt_2;
    double a_r, b_r, a_angle, b_angle;

    subPt_1 = a - p_min, subPt_2 = b - p_min;

    a_angle = atan2(subPt_1.y, subPt_1.x);
    b_angle = atan2(subPt_2.y, subPt_2.x);

    if (a_angle < b_angle) {return true;}
    if (a_angle > b_angle) {return false;}

    a_r = subPt_1.x * subPt_1.x * subPt_1.y * subPt_1.y; 
    b_r = subPt_2.x * subPt_2.x * subPt_2.y * subPt_2.y; 

    return (a_r < b_r); // return (a_r <= b_r); // Code crashes, saying invalid operator <. I do not know why. Pl tell me.
}

auto it  = std::min_element(V.begin(), V.end(), lex_yx);
std::sort(  V.begin(), V.end(), boost::bind(CalcAngRad_Compare, *it, _1, _2)); 

当我传入一个简单的测试输入比如

2 2
3 3
4 4
1 1 // this should be first element in the sorted array
5 5

它有效,我得到了排序数组。但现在看看另一个简单的输入:

-0.5    -0.1
-0.1    -0.1
0       1
-1      -0.1 // this should be first element is sorted array. BUT IT IS NOT!!
5       5

我不认为我做错了什么。一个问题可能是当 y 坐标相等时,角度为 0 或 pi。在这种情况下,如果半径也相等,那么我return false。我试图将其更改为return (a_r &lt;= b_r),但这似乎是不可能的。代码崩溃说无效的运算符 xutility 中,此行为true:else if (_Pred(_Right, _Left))。我不明白正在测试什么,可能检查了一些优先级。)

我希望你回答:

  1. 如何解决该问题并始终获得(正确)排序的向量
  2. 还有其他方法可以实现我正在做的事情吗?
  3. 我非常有兴趣了解我的思维/实施方式有什么问题?

【问题讨论】:

  • 第二批输入的完整列表顺序是什么?
  • atan2 的返回值可能为负数(最高为 -pi)。也许你应该在比较之前取绝对值?否则p_min 的角度可能为零,但与另一点的角度可能为负,因此更小。
  • @norlesh 它包含超过 2k 个点,我已将问题最小化到这个小列表。如果解决了这个问题,整个列表就可以了..
  • @jogojapan 是的,你是对的,atan2 的范围可达 -pi,但绝对不是一个选项。它引起了其他情况..

标签: c++ sorting boost stl std


【解决方案1】:

如何解决问题并始终获得(正确)排序的向量 还有其他方法可以实现我正在做的事情吗?

不确定您想要的顺序,less_yx 可以完成这项工作吗?

我非常有兴趣了解我的思维/实施方式有什么问题?

假设 IEEE 浮点算法(否则 0, 0 存在域错误)

由于您的参考点是最低的y,因此传递给 atan2 的第二个参数是正数或空值。

- `atan2(0, 0) = 0`
- `atan2(+x, 0) = 0`
- `atan2(x, +y) > 0`

因此,根据atan2,任何最低的 y 都是等价的。

a_r = subPt_1.x * subPt_1.x * subPt_1.y * subPt_1.y;

对于subPt_1.y == 0,始终为0。 你是说

a_r = subPt_1.x * subPt_1.x + subPt_1.y * subPt_1.y;

相反? (所以它比较x,即使y == 0

【讨论】:

  • 你在金钱上是对的,最愚蠢的错误:a_rb_r。更正了,问题就没有了..你的眼睛很好..谢谢
【解决方案2】:

您的第一个排序标准是由atan2 计算的角度。在您的情况下,p_min 是-1.0/-0.1。如果a 是-1.0/-0.1 和b 是-0.5/-0.1,您将计算atan2( -0.1 + 0.1, -1.0 + 1.0 ); (a-p_min) 和atan2( -0.1 + 0.1, -0.5 + 1.0 ); (b-p_min),这两种情况下都是0.0。您继续将0.0 * 0.0 * 0.0 * 0.0 (a) 与0.5 * 0.5 * 0.0 * 0.0(b) 进行比较,这意味着0.0 &lt; 0.0。因此a (-1.0/-0.1) 不小于b(-0.5/-0.1)。

要计算向量之间的角度,请使用点积。 |一个| * |B| * cos(A 和 B 之间的角度) = Ax * Bx + Ay * By

double len_p_min = sqrt( p_min.x*p_min.x + p_min.y*p_min.y ); // lenght of p_min
double len_a = sqrt( a.x*a.x + a.y*a.y ); // lenght of a
double len_b = sqrt( b.x*b.x + b.y*b.y ); // lenght of b

Point normalizedMin{ p_min.x / len_p_min, p_min.y / len_p_min }; // direction of p_min ( lenght == 1.0)
Point normalizedA{ a.x / len_a, a.y / len_a }; // direction of a ( lenght == 1.0)
Point normalizedB{ b.x / len_b, b.y / len_b }; // direction of b ( lenght == 1.0)

double cosAmin = normalizedMin.x*normalizedA.x + normalizedMin.y*normalizedA.y; // cos of angle between a and p_min
double angleAmin = acos( cosAmin );

double cosBmin = normalizedMin.x*normalizedB.x + normalizedMin.y*normalizedB.y; // cos of angle between b and p_min
double angleBmin = acos( cosBmin );

要比较角度,无需计算acos。比较角度的余弦就足够了

if ( fabs( cosAmin ) < fabs( cosBmin ) ) {return true;}
if ( fabs( cosAmin ) > fabs( cosbmin ) ) {return false;}

如果您想按长度对向量进行排序,请使用 |v| = sqrt( v.x * v.x + v.y * v.y ) 如果只需要比较向量的长度,就不需要计算sqrt,比较长度^2就足够了。

a_r = subPt_1.x * subPt_1.x + subPt_1.y * subPt_1.y; // |a|^2 (lenght of a pow 2)
b_r = subPt_2.x * subPt_2.x + subPt_2.y * subPt_2.y; // |b|^2 (lenght of b pow 2)

return (a_r < b_r);

【讨论】:

  • 在第二个条件中,OP比较的是相对位置,而不是绝对位置,所以0.025 &lt; 0.01变成0 == 0
  • 感谢您的努力。我也同意你的建议。但是我们有一些东西要针对一种特定的算法来证明,所以不能用 acos/cos 代替 atan。不过谢谢你..我没有想到这种方式..
猜你喜欢
  • 2020-10-02
  • 1970-01-01
  • 1970-01-01
  • 2012-04-09
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多