C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でジャービスマーチ(Jarvis March)を実装して凸包を求める方法

ジャービスマーチ(Jarvis March)アルゴリズムは、与えられた点の集合から凸包(Convex Hull)の頂点、すなわち境界となる角の点を検出するための手法です。

まずデータセットの中で最も左側にある点を起点とし、そこから反時計回りに回転しながら凸包に含まれる点を順番に選んでいきます。現在の点から次の点を選ぶ際には、各候補点の方向(向き)を外積によって判定し、角度が最大になる点を採用します。すべての点を巡り、次の点が再び始点に戻った時点でアルゴリズムを終了します。

入力:点の集合 {(-7,8), (-4,6), (2,6), (6,4), (8,6), (7,-2), (4,-6), (8,-7),(0,0), (3,-2),(6,-10),(0,-6),(-9,-5),(-8,-2),(-8,0),(-10,3),(-2,2),(-10,4)}
出力:凸包の境界点は以下の通り
(-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)

アルゴリズム

findConvexHull(points, n)

入力:点の配列、点の個数 n。

出力:凸包の頂点(角となる点)の集合。

Begin
    start := points[0]
    for each point i, do
        if points[i].x < start.x, then   // 最も左にある点を取得
            start := points[i]
    done
    current := start
    始点を結果集合に追加する。
    共線点を格納するための colPts 集合を定義する。
    while true, do   // 無限ループを開始
        next := points[i]
        0番目以外のすべての点 i について、
        if points[i] = current, then
            以降の処理をスキップして次の反復へ
            val := current, next, points[i] の外積
        if val > 0, then
            next := points[i]
            colPts 配列をクリアする
        else if val = 0, then
            if next が points[i] より current に近い場合, then
                next を colPts に追加
                next := points[i]
            else
                points[i] を colPts に追加
    done
    colPts 内のすべての要素を結果に追加する。
    if next = start, then
        ループを抜ける
    next を結果に挿入する。
    current := next
    done
    return result
End

サンプルコード

#include<iostream>
#include<set>
#include<vector>
using namespace std;
struct point {    // 2次元平面上の点を定義
    int x, y;
    bool operator==(point p2) {
        if(x == p2.x && y == p2.y)
            return 1;
        return 0;
    }
    bool operator<(const point &p2)const {    // set内でソートするためのダミー比較関数
        return true;
    }
};
int crossProduct(point a, point b, point c) {    // ベクトルabに対するcの位置を求める
    int y1 = a.y - b.y;
    int y2 = a.y - c.y;
    int x1 = a.x - b.x;
    int x2 = a.x - c.x;
    return y2*x1 - y1*x2; // 結果が負ならcは左側、正なら右側、0ならa,b,cは同一直線上(共線)
}
int distance(point a, point b, point c) {
    int y1 = a.y - b.y;
    int y2 = a.y - c.y;
    int x1 = a.x - b.x;
    int x2 = a.x - c.x;
    int item1 = (y1*y1 + x1*x1);
    int item2 = (y2*y2 + x2*x2);
    if(item1 == item2)
        return 0; // bとcがaから等距離の場合
    else if(item1 < item2)
        return -1; // bの方がaに近い場合
        return 1; // cの方がaに近い場合
}
set<point> findConvexHull(point points[], int n) {
    point start = points[0];
    for(int i = 1; i<n; i++) {    // 開始用に最も左の点を探す
        if(points[i].x < start.x)
            start = points[i];
    }
    point current = start;
    set<point> result;    // 重複した点の登録を避けるためにsetを使用
    result.insert(start);
    vector<point> *collinearPoints = new vector<point>;
    while(true) {
        point nextTarget = points[0];
        for(int i = 1; i<n; i++) {
            if(points[i] == current) // 選択された点が現在の点なら以降を無視
                continue;
                int val = crossProduct(current, nextTarget, points[i]);
                if(val > 0) {    // i番目の点が左側にある場合
                    nextTarget = points[i];
                    collinearPoints = new vector<point>; // 共線点をリセット
                }else if(val == 0) {    // 3点が同一直線上にある場合
                    if(distance(current, nextTarget, points[i]) < 0) {    // 近い方を共線リストに追加
                        collinearPoints->push_back(nextTarget);
                        nextTarget = points[i];
                    }else{
                        collinearPoints->push_back(points[i]); // i番目の点がnextTargetと同じかより近い場合
                    }
                }
        }
        vector<point>::iterator it;
        for(it = collinearPoints->begin(); it != collinearPoints->end(); it++) {
            result.insert(*it); // 共線点をすべて結果集合に追加
        }
        if(nextTarget == start) // 次の点が始点なら一周したことを意味する
            break;
        result.insert(nextTarget);
        current = nextTarget;
    }
    return result;
}
int main() {
    point points[] = {{-7,8},{-4,6},{2,6},{6,4},{8,6},{7,-2},{4,-6},{8,-7},{0,0},
        {3,-2},{6,-10},{0,-6},{-9,-5},{-8,-2},{-8,0},{-10,3},{-2,2},{-10,4}};
    int n = 18;
    set<point> result;
    result = findConvexHull(points, n);
    cout << "Boundary points of convex hull are: "<<endl;
    set<point>::iterator it;
    for(it = result.begin(); it!=result.end(); it++)
        cout << "(" << it->x << ", " <<it->y <<") ";
}

出力結果

Boundary points of convex hull are:
(-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)

  1. C++で平行四辺形の面積を求めるプログラムの作成方法

    この記事では、平行四辺形の底辺と高さを表す2つの値が与えられたとき、C++を使ってその面積を求めるプログラムを作成する方法を解説します。 平行四辺形とは? 平行四辺形とは、4つの辺からなる閉じた図形であり、向かい合う2組の辺がそれぞれ長さが等しく、互いに平行になっている四角形のことです。 問題を理解するための具体例 入力 B = 20, H = 15 出力 300 説明 平行四辺形の面積 = 底辺 × 高さ = 20 × 15 = 300 解決アプローチ この問題を解くには、平行四辺形の面積を求める幾何学の公式を使用します。 面積 = 底辺 × 高さ つまり、与えられた底辺と高さを掛け合わせ

  2. ヴィジュネル暗号をC++で実装する方法|暗号化・復号化プログラムの解説

    ヴィジュネル暗号(Vigenère Cipher)は、アルファベットのテキストを暗号化するための多表式換字暗号の一種です。鍵の各文字に応じて異なる換字表が切り替わる仕組みのため、単純なシーザー暗号などと比べて、頻度分析による解読への耐性が高いという特徴があります。 この方式の暗号化と復号化には「ヴィジュネル暗号表」を使用します。これは、AからZまでのアルファベットを1行ずつ順にずらしながら26行に並べた、26×26の表です。 暗号化の流れ 鍵:WELCOME 平文:Thisistutorialspoint まず、与えられた鍵を平文と同じ長さに達するまで繰り返し、処理用の鍵列を作成します。