【问题标题】:Bug in C++ convex hull jarvis march algorithm implementation?C++ 凸包 jarvis March 算法实现中的错误?
【发布时间】:2019-05-03 16:37:32
【问题描述】:

我为 C++ 凸包算法编写了一个实现,这应该是微不足道的。我下面的代码遵循the paperwork 的公式/方法。我使用名为“Jarvis March”的方法。

它在 20000 点上工作得很好,而在性能方面却少得多,但如果我随机化输入点数组的顺序(使用 std::shuffle),我可以看到有时它会显示错误

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

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

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

代码:

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;
}

根据图片:

类型,要明确:

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;
};

最后,我没有看到:这段代码的具体错误在哪里?

【问题讨论】:

    标签: c++ algorithm convex-hull


    【解决方案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 获得。

    【讨论】:

      【解决方案2】:

      首先观察你如何在q 和i 之间进行选择。假设你有这个设置:

      如果(i-p)^(q-p) &lt; 0,您想选择i 而不是q。但是你转而向右转。选择p = (0,0), q = (1,0), i = (0,1)很容易看出来:

      i
      |
      |
      p-----q
      

      然后(i-p)^(q-p) = (0,1)^(1,0) = 0 - 1 = -1 &lt; 0,应该选择i而不是q。

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

      但这一切都无所谓。该算法运行良好。您可以找到它here。它会为您的洗牌数组生成正确的答案。

      【讨论】:

      • @Croll 感谢您提出一个有趣的问题。你发现错误了吗?很有趣。
      • 是的。查看答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-28
      • 2014-09-26
      • 2013-03-28
      • 2014-11-29
      • 2011-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多