【问题标题】:Why doesn't this implementation of Jarvis' March ("Gift wrapping algorithm") work?为什么 Jarvis 的 March(“礼品包装算法”)的实现不起作用?
【发布时间】:2010-10-19 19:50:12
【问题描述】:

我正在尝试实现 Jarvis 的算法来查找一组点的凸包,但由于某种原因它不起作用。这是我的实现:

procedure TPointList.ConvexHull(aHull : TPointList); //Return the convex hull of a set of 2D points
var
  vPointOnHull  : TPoint2D;
  vEndpoint     : TPoint2D;
  I             : integer;
begin
  aHull.Clear;
  if Count < 3 then exit;

  vPointOnHull := Self.LeftMostPoint;
  repeat
    aHull.Add(vPointOnHull);
    vEndpoint := Self.Point[0];

    for I := 1 to Self.Count-1 do
      if Orientation(vPointOnHull,vEndpoint,Self.Point[I]) = LeftHandSide then
        vEndpoint := Self.Point[I];

    vPointOnHull := vEndpoint;
  until vEndpoint = aHull.Point[0];
end;
  • TPointList 是一个简单的点列表。
  • Orientation 是 Arash Partow 的库“FastGEO”中的一个函数
  • 该实现或多或少直接来自Wikipedia article on the algorithm

发生的情况是该方法开始一遍又一遍地向 aHull 添加相同的点。在一个测试用例中,我发送点 (200;200) (300;100) (200;50) 和 (100;100),算法首先将 (100;100) 添加到正确的 aHull 中,但随后它开始一遍又一遍地添加 (200;200)。

显然,我在实施过程中做错了什么,但对于我的生活,我看不出是什么。

更新:

Jonathan Dursi 让我走上了正轨。这一行

if Orientation(vPointOnHull,vEndpoint,Self.Point[I]) = LeftHandSide then    

应该换成这个

if (vPointOnHull = vEndpoint) or (Orientation(vPointOnHull,vEndpoint,Self.Point[I]) = LeftHandSide) then

像魅力一样工作:-)

【问题讨论】:

    标签: algorithm delphi geometry


    【解决方案1】:

    (200;200) 是点 0 可能不是巧合。

    看起来您并未将当前点 (vPointOnHull) 排除在终点 (vEndPoint) 之外,并且您的 Orientation 实现不会拒绝这种情况;如果叉积为正,大概它返回 LHS,如果 vPointOnHull == vEndPoint,叉积为零,所以永远不要 LHS。因此,一旦选择了 Point 0,就没有任何东西可以取代 Point 0,等等。

    在这种情况下,您可以修改方向以返回“退化”或其他内容,并拒绝该点,或者您可以将当前点排除在终点之外。请注意,您不想做明显的事情,在行进时从点集中过滤掉当前的CH点,因为您需要找到结束点是关闭循环的第一个点。

    更新:看看 FastGEO 的东西,可能更新 Orientation 不是要走的路(尽管在这个算法中应该多考虑一下共线点的情况;如果有是船体上的共线点,你真的想要最接近的点,所以你想要一个 else if Orientation = Collinear then.. update vEndpoint if new point is closer 子句在 if 语句之后)。

    最简单的方法可能是添加几行来跟踪当前指标,以便您可以轻松测试相等性:有点像

    iPointOnHull := Self.IndexOfLeftMostPoint;
    vPointOnHull := Self.LeftMostPoint
    ...
    vEndpoint := Self.Point[0];
    iEndPoint := 0;
    if (iPointOnHull = 0) then 
    begin
        vEndPoint := Self.Point[1];
        iEndPoint := 1;
    end
    ...
    vPointOnHull := vEndPoint;
    iPointOnHull := iEndPoint;
    

    【讨论】:

    • 我找到了一种方法,但你帮我发现了它。谢谢:-)
    【解决方案2】:

    循环使用这行代码添加:

    aHull.Add(vPointOnHull);
    

    vPointOnHull 仅在这些行中分配:

    vPointOnHull := Self.LeftMostPoint;
    vPointOnHull := vEndpoint;
    

    你已经解释了LeftMostPoint被正确添加了,所以repeat必须来自vEndPoint,这是在这些行中分配的:

    vEndpoint := Self.Point[0];
    vEndpoint := Self.Point[I];
    

    所以我猜最后一个赋值(在下面的 if 语句中)永远不会到达。

      if Orientation(vPointOnHull,vEndpoint,Self.Point[I]) = LeftHandSide then
        vEndpoint := Self.Point[I];
    

    --杰罗恩

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-28
      • 2019-05-03
      • 2012-04-18
      • 2011-12-07
      • 2023-03-21
      • 2017-02-09
      • 2015-11-25
      • 2021-12-15
      相关资源
      最近更新 更多