C ++凸包jarvis前进算法中的错误?

时间:2018-12-01 23:14:48

标签: c++ algorithm convex-hull

我写了一个C ++凸包算法的实现,它应该很简单。我的以下代码遵循the paperwork中的公式/方法。我使用称为“贾维斯进行曲”的方法。

它可以正常工作20000点,而性能却要差得多,但是如果我随机化输入点数组的顺序(使用using namespace std; vector<Point> m_in; inline double cross(const Point &a, const Point &b) { return (a.x * b.y) - (b.x * a.y); } // conterclockwise test inline bool CCW(const Point &p, const Point &i, const Point &q) { // auto a = p.x, // b = p.y, // c = i.x, // d = i.y, // e = q.x, // f = q.y; // the same: // return ((f - b) * (c - a)) > ((d - b) * (e - a)); // the same: // Point va { c - a, d - b }; // i - p // Point vb { e - a, f - b }; // q - p // return cross(va, vb) > 0; // the same, compact: return cross(i - p, q - p) > 0; } void Reset(vector<Point> &in) { m_in = move(in); } vector<Line> GetLine() const { vector<Line> res; Point l = m_in.front(); for(auto &i : m_in) { if(l.x < i.x) { l = i; } } Point p = l; for(auto &pi : m_in) { Point q = pi; for(auto &i : m_in) { if(CCW(p, i, q)) { q = i; } } res.push_back(Line { p, q }); p = q; } return res; } ),我会发现有时它会显示错误

enter image description here

对相同点的输入向量进行混洗后:

enter image description here

(绿线是针对给定的黑点计算出的凸包)

您可能会认为该错误与“最后”凸包线有关。但这有些不同,在这里可以观察到:

I don't really know why the diagonal line looks different it is rendering bug i assume

代码:

struct Point
{
    double x, y;

    friend Point operator+(const Point& a, const Point& b);
    friend Point operator-(const Point& a, const Point& b);
    friend bool operator!=(const Point& a, const Point& b);
};

struct Line
{
    Point a;
    Point b;
};

基于图片:

enter image description here

类型,要明确:

# Density overlayed with Scatter
#Set figure size
plt.figure(figsize=(10,6))

# Plots
sns.kdeplot(df.Attack,
            df.Defense)

sns.lmplot(x='Attack',
          y='Defense',
          hue='Stage',
          data=df,
          fit_reg=False)

plt.title('Density vs Scatter')

最后,我看不到:此代码中的特定错误在哪里?

2 个答案:

答案 0 :(得分:1)

第一个观察者:您如何在qi之间进行选择。假设您已进行以下设置:

enter image description here

如果是i,则要在q上选择(i-p)^(q-p) < 0。但是,您转向右侧。如果选择p = (0,0), q = (1,0), i = (0,1),就很容易看到它:

i
|
|
p-----q

然后(i-p)^(q-p) = (0,1)^(1,0) = 0 - 1 = -1 < 0,并且应该选择i上的q

还要注意,您是从x最大的点开始的,而不是从最小的点开始的。

但是所有这些都没有关系。该算法可以正常工作。。您可以here找到它。它会为您的混洗数组提供正确的答案。

答案 1 :(得分:1)

更正CCW测试代码:

inline bool CCW(const Point &p, const Point &i, const Point &q)
{
    return cross(i - p, q - p) < 0.0;
}

请勿手动遍历输入数组以找到最低的X坐标。使用std::sort()通过X坐标对输入数组进行排序。这不会破坏该方法的纸面描述。

void Reset(vector<Point> &in)
{
    sort(in.begin(), in.end(), [](const Point &a, const Point &b) { return a.x < b.x; });

    m_in = move(in);
}

重写代码,使其使用迭代器(算法描述中令人困惑的行是q = p + 1,而这实际上并未在OP的代码中实现)。尝试保存原始的语法方法,因为没有人喜欢在其他地方广泛监督的C风格或C ++ 98示例。

vector<Line> GetLine() const
{
    vector<Line> res;

    if(m_in.empty())
    {
        return res;
    }

    auto    l = m_in.begin(),
            r = m_in.end() - 1;

    auto p = l;
    do
    {
        auto q = p + 1;
        if(q > r) {
            q = l;
        }

        for(auto i = l; i <= r; i++)
        {
            if(CCW(*p, *i, *q)) {
                q = i;
            }
        }

        res.push_back(Line{*p, *q});
        p = q;
    }
    while(p != l);

    return res;
}

如果您有兴趣,可以在Github上找到我应用的完整代码。