計算幾何学(1)凸包

計算幾何学(1)凸包

三角形の面積を解く:Helen-Qin Jiushao式

特定の点が特定の直線の左側にあると判断する:上記のヘレンの式を使用

bool ToLeft(Point p, Point q, Point s)
{
    return Area2(p, q, s) > 0;
}

int Area2(Point p, Point q, Point s)
{
    return 
        p.x * q.y - p.y * q.x 
       +q.x * s.y - q.y * s.x
       +s.x * p.y - s.y *p.x;
}

点が三角形の内側にあるかどうかを判断します。3つの点が反時計回りに接続され、有向エッジを形成します。点が3つの直線の左側にある場合、それは真です。(複雑さはO(n ^ 4)です)

bool InTriangle(Point p, Point q, Point r, Point s)
{
    bool pqLeft = ToLeft(p, q, s);
    bool qrLeft = ToLeft(q, r, s);
    bool rpLeft = ToLeft(r, p, s);
    return (pdLeft == qrLeft) && (qrLeft == rqLeft);
}

すべての極を見つける

1.この点に沿って直線を描きます。直線が見つかる必要があります。それにより、他のすべての点が線の片側にあり、極であるかどうかを判断します(複雑度はO(n ^ 3)です)。

void markEE(Point S[], int n) //n>2
{
    for(int k = 0; k <n; k ++)
        S[k].extreme = False; //先假设所有的点都不是极点
    for(int p = 0; p < n; p ++)
        for(int q = p + 1; q < n; q ++)
            checkEdge(S, n, p, q);
}

void checkEdge(Point S[], int n, int p, int q)
{
    bool LEmpty = True, REmpty = True;
    for(int k = 0; k < n && (LEmpty || REmpty); k ++)
    {
        if(k != p && k != q)
        {
            ToLeft(S[p], S[q], S[k]) ? LEmpty = False : REmpty = False;
        }
    }
    if(LEmpty || REmpty)
        S[p].extreme = S[q].extreme = True;
}

2.ジャービスアルゴリズム:ここでは、反時計回りを正の方向として定義します。あるポイントの先行は反時計回りの次のポイントであり、後続は時計回りの次のポイントです。Javis Marchアルゴリズムは出力に依存しており、その複雑さは凸包のスケールによって変化します。

void Jarvis(Point S[], int n)
{
    for(int k = 0; k < n; k ++)
        S[k].extreme = false;  //假设所有的点都不是极点
    int ltl = LTL(S, n); int k = ltl;  //先利用LTL算法找到第一个极点,k是起点
    do
    {
        P[k].extreme = true; int s = -1;  //s是要找的下一个极点,用t去循环找
        for(int t = 0; t < n; t ++)
        {
            if(t != k && t != s && (s == -1 || !ToLeft(P[k], P[s], P[t])) 
                s = t; //如果t在pq的右边,更新s
        }
        P[k].succ = s; k = s; //新的极边pq确定
    }while(ltl != k); //如果循环回到了原点,结束
}

int LTL(Point S[], int n)
{
    int ltl = 0;
    for(int k = 1; k < n; k ++)
    {
        if(P[k].y < P[ltl].y || (P[k].y == P[ltl].y && P[k].x < P[ltl].x)
            ltl = k;
    }
    return ltl;
}

特定の点がポリゴンの内側にあるかどうかを判断します。x点の任意の点vと元の凸包に対して光線を作成します。その後、vに先行と後続があり、先行と後続の両方が光線の右側または左側にある場合は、接線です。 。そのような接線が見つからない場合、点は凸包の内側にあります。

3.グラハムスキャンアルゴリズム:最初に極エッジ(LTL)があり、残りのポイントは極角に従って小から大に並べ替えられます。SスタックとTスタックがあり、最初はポイント1とポイント2がSスタックにあり、他のポイントはTスタックの一番上から一番下までです。コアコード:Tスタックの最上部のポイントがSスタックの最上部の2つのポイントの左側と2番目のポイントの左にある場合、Tスタックの最上部のポイントをSスタックに押し込みます。それ以外の場合は、Sスタックの最上部のポイントをポップします。プログラムの最後にあるSスタックのすべての要素はすべて極です。

while(!T.empty())
{
    toLeft(S[1],S[0],T[0]) ? S.push(T.pop()) : S.pop();
}

4. Divided-And-Conquer(Divided-And-Conquer):複数のポイントを適切にグループ化し、サブ凸包を個別に作成して、それらを凸包にマージします。

上の図では、2つのサブ凸包の極がコアとは別に注文されているため、これらの点を全体的な順序に変換するために必要な方法は2つだけであり、グラハムスキャンが実行されるため、これはより良い状況です。

2つのカットポイントを見つけるだけで、tsセグメントは役に立たなくなります。次に、tsを破棄した後、stセグメントと別のサブ凸包のポイントで2ウェイマージを実行してから、Graham Scanを実行します。

まず、2つのサブ凸包が作成されると、それらを縦線で区切ることができます。2つのサブ凸包をマージするには、2つの接線を見つけるだけです。実際の操作では、最初に左のサブ凸包の右端の点を見つけ、次に右のサブ凸包の左端の点を見つけます。ここでの検索方法は、サブ凸包を段階的に構築するときに左端と右端の点を記録するだけでよく、必要なときにそれを見つけるのにO(1)時間しかかかりません。それ以外の場合は使用する必要があります。これら2つのポイントを見つけるO(n)時間。

おすすめ

転載: blog.csdn.net/a40850273/article/details/90543433