中心の周りの円の点を取得するための効率的なアルゴリズム


8

問題

特定のポイントについて、特定の半径の円内にあるすべてのピクセルを取得したいのですが、ポイントは整数座標しか持てません。つまり、キャンバス内のピクセルです。

図

だから私は、与えられた黄色の領域内のすべてのポイントを取得したい(x, y)r

アプローチ

私が考えることができる最も効率的な方法は、正方形をループして各ポイントのユークリッド距離(x, y)確認することです。

for (int px = x - r; px <= x + r; px++) {
  for (int py = y - r; py <= y + r; py++) {
    int dx = x - px, dy = y - py;

    if (dx * dx + dy * dy <= r * r) {
      // Point is part of the circle.
    }
  }
}

ただし、これは、このアルゴリズムが(r * 2)^2 * (4 - pi) / 4円の一部ではないピクセルをチェックすることを意味します。dx * dx + dy * dy <= r * rは、かなり高価に見えます1 / 4が、ほとんどの場合、重複して呼び出されます。

ここ提案されているようなものを統合すると、パフォーマンスが向上する可能性があります。

for (int px = x - r; px <= x + r; px++) {
  for (int py = y - r; py <= y + r; py++) {
    int dx = abs(x - px), dy = abs(y - py);

    if (dx + dy <= r || (!(dx > r || dy > r) && (dx * dx + dy * dy <= r * r))) {
      // Point is part of the circle.
    }
  }
}

ただし、作成者自身が指摘したように、ほとんどの点が円の内側にある場合(特にabs)のpi / 4場合、これは高速にはなりません。


この質問に関するリソースは見つかりませんでした。SQLではなく、C ++のソリューションを具体的に探しています


1
整数の乗算と加算はかなり高速です。余分な比較をたくさん追加すると効果があるかどうかはわかりませんが、特に、分岐予測ミスが発生する可能性があるためです。
フランソワアンドリュー

「最高」または「最高」を尋ねる質問は、単一の最良の回答が存在することはめったにないため、通常、明確に回答することはできません。どのソリューションが最適であるかは、実際のユースケースとそれが実行されるアーキテクチャーに応じて異なります。
フランソワアンドリュー

3
与えられたy値のx値の範囲を知ることは、前と次のy値のx値の範囲への洞察を提供するという事実を利用してください。
スコットハンター

1
前のポイントが良好である次のポイントから不良である次のポイントに移動したら、少なくともループから抜け出すことができるはずです。その時点で、境界を越えたことがわかり、その線上に良い点が残っていません。
NathanOliver

1
スコッツの例を作成するための別のオプションは、上部または下部の中央から開始し、境界に達するまで左および右にループします。これは、境界を通過するとすぐに内部ループが停止することを意味します。
NathanOliver

回答:


4

約束したベンチマークはここにあります。

セットアップ

私はグーグルベンチマークを使用し、円の境界内のすべてのポイントをに挿入することがタスクでしたstd::vector<point>。一連の半径と一定の中心のベンチマーク:

radii = {10, 20, 50, 100, 200, 500, 1000}
center = {100, 500}
  • 言語:C ++ 17
  • コンパイラ:msvc 19.24.28316 x64
  • プラットフォーム:Windows 10
  • 最適化:O2(完全最適化)
  • スレッド化:シングルスレッド実行

各アルゴリズムの結果が正しいかどうかがテストされます(OPsアルゴリズムの出力と比較)。

これまでのところ、次のアルゴリズムがベンチマークされています。

  1. OPのアルゴリズムenclosing_square
  2. 私のアルゴリズム containing_square
  3. creativecreatorormaybenotのアルゴリズム edge_walking
  4. Mandy007のアルゴリズム binary_search

結果

Run on (12 X 3400 MHz CPU s)
CPU Caches:
  L1 Data 32K (x6)
  L1 Instruction 32K (x6)
  L2 Unified 262K (x6)
  L3 Unified 15728K (x1)
-----------------------------------------------------------------------------
Benchmark                                   Time             CPU   Iterations
-----------------------------------------------------------------------------
binary_search/10/manual_time              804 ns         3692 ns       888722
binary_search/20/manual_time             2794 ns        16665 ns       229705
binary_search/50/manual_time            16562 ns       105676 ns        42583
binary_search/100/manual_time           66130 ns       478029 ns        10525
binary_search/200/manual_time          389964 ns      2261971 ns         1796
binary_search/500/manual_time         2286526 ns     15573432 ns          303
binary_search/1000/manual_time        9141874 ns     68384740 ns           77
edge_walking/10/manual_time               703 ns         5492 ns       998536
edge_walking/20/manual_time              2571 ns        49807 ns       263515
edge_walking/50/manual_time             15533 ns       408855 ns        45019
edge_walking/100/manual_time            64500 ns      1794889 ns        10899
edge_walking/200/manual_time           389960 ns      7970151 ns         1784
edge_walking/500/manual_time          2286964 ns     55194805 ns          308
edge_walking/1000/manual_time         9009054 ns    234575321 ns           78
containing_square/10/manual_time          629 ns         4942 ns      1109820
containing_square/20/manual_time         2485 ns        40827 ns       282058
containing_square/50/manual_time        15089 ns       361010 ns        46311
containing_square/100/manual_time       62825 ns      1565343 ns        10990
containing_square/200/manual_time      381614 ns      6788676 ns         1839
containing_square/500/manual_time     2276318 ns     45973558 ns          312
containing_square/1000/manual_time    8886649 ns    196004747 ns           79
enclosing_square/10/manual_time          1056 ns         4045 ns       660499
enclosing_square/20/manual_time          3389 ns        17307 ns       206739
enclosing_square/50/manual_time         18861 ns       106184 ns        37082
enclosing_square/100/manual_time        76254 ns       483317 ns         9246
enclosing_square/200/manual_time       421856 ns      2295571 ns         1654
enclosing_square/500/manual_time      2474404 ns     15625000 ns          284
enclosing_square/1000/manual_time     9728718 ns     68576389 ns           72

コード

完全なテストコードは以下のとおりです。コピーして貼り付け、自分でテストできます。fill_circle.cppさまざまなアルゴリズムの実装が含まれています。

main.cpp

#include <string>
#include <unordered_map>
#include <chrono>

#include <benchmark/benchmark.h>

#include "fill_circle.hpp"

using namespace std::string_literals;

std::unordered_map<const char*, circle_fill_func> bench_tests =
{
    {"enclosing_square", enclosing_square},
    {"containing_square", containing_square},
    {"edge_walking", edge_walking},
    {"binary_search", binary_search},
};

std::vector<int> bench_radii = {10, 20, 50, 100, 200, 500, 1000};

void postprocess(std::vector<point>& points)
{
    std::sort(points.begin(), points.end());
    //points.erase(std::unique(points.begin(), points.end()), points.end());
}

std::vector<point> prepare(int radius)
{
    std::vector<point> vec;
    vec.reserve(10ull * radius * radius);
    return vec;
}

void bm_run(benchmark::State& state, circle_fill_func target, int radius)
{
    using namespace std::chrono;
    constexpr point center = {100, 500};

    auto expected_points = prepare(radius);
    enclosing_square(center, radius, expected_points);
    postprocess(expected_points);

    for (auto _ : state)
    {
        auto points = prepare(radius);

        auto start = high_resolution_clock::now();
        target(center, radius, points);
        auto stop = high_resolution_clock::now();

        postprocess(points);
        if (expected_points != points)
        {
            auto text = "Computation result incorrect. Expected size: " + std::to_string(expected_points.size()) + ". Actual size: " + std::to_string(points.size()) + ".";
            state.SkipWithError(text.c_str());
            break;
        }

        state.SetIterationTime(duration<double>(stop - start).count());
    }
}

int main(int argc, char** argv)
{
    for (auto [name, target] : bench_tests)
        for (int radius : bench_radii)
            benchmark::RegisterBenchmark(name, bm_run, target, radius)->Arg(radius)->UseManualTime();

    benchmark::Initialize(&argc, argv);
    if (benchmark::ReportUnrecognizedArguments(argc, argv))
        return 1;
    benchmark::RunSpecifiedBenchmarks();
}

fill_circle.hpp

#pragma once

#include <vector>

struct point
{
    int x = 0;
    int y = 0;
};

constexpr bool operator<(point const& lhs, point const& rhs) noexcept
{
    return lhs.x != rhs.x
               ? lhs.x < rhs.x
               : lhs.y < rhs.y;
}

constexpr bool operator==(point const& lhs, point const& rhs) noexcept
{
    return lhs.x == rhs.x && lhs.y == rhs.y;
}

using circle_fill_func = void(*)(point const& center, int radius, std::vector<point>& points);

void enclosing_square(point const& center, int radius, std::vector<point>& points);
void containing_square(point const& center, int radius, std::vector<point>& points);
void edge_walking(point const& center, int radius, std::vector<point>& points);
void binary_search(point const& center, int radius, std::vector<point>& points);

fill_circle.cpp

#include "fill_circle.hpp"

constexpr double sqrt2 = 1.41421356237309504880168;
constexpr double pi = 3.141592653589793238462643;

void enclosing_square(point const& center, int radius, std::vector<point>& points)
{
    int sqr_rad = radius * radius;

    for (int px = center.x - radius; px <= center.x + radius; px++)
    {
        for (int py = center.y - radius; py <= center.y + radius; py++)
        {
            int dx = center.x - px, dy = center.y - py;
            if (dx * dx + dy * dy <= sqr_rad)
                points.push_back({px, py});
        }
    }
}

void containing_square(point const& center, int radius, std::vector<point>& points)
{
    int sqr_rad = radius * radius;
    int half_side_len = radius / sqrt2;
    int sq_x_end = center.x + half_side_len;
    int sq_y_end = center.y + half_side_len;

    // handle inner square
    for (int x = center.x - half_side_len; x <= sq_x_end; x++)
        for (int y = center.y - half_side_len; y <= sq_y_end; y++)
            points.push_back({x, y});

    // probe the rest
    int x = 0;
    for (int y = radius; y > half_side_len; y--)
    {
        int x_line1 = center.x - y;
        int x_line2 = center.x + y;
        int y_line1 = center.y - y;
        int y_line2 = center.y + y;

        while (x * x + y * y <= sqr_rad)
            x++;

        for (int i = 1 - x; i < x; i++)
        {
            points.push_back({x_line1, center.y + i});
            points.push_back({x_line2, center.y + i});
            points.push_back({center.x + i, y_line1});
            points.push_back({center.x + i, y_line2});
        }
    }
}

void edge_walking(point const& center, int radius, std::vector<point>& points)
{
    int sqr_rad = radius * radius;
    int mdx = radius;

    for (int dy = 0; dy <= radius; dy++)
    {
        for (int dx = mdx; dx >= 0; dx--)
        {
            if (dx * dx + dy * dy > sqr_rad)
                continue;

            for (int px = center.x - dx; px <= center.x + dx; px++)
            {
                for (int py = center.y - dy; py <= center.y + dy; py += 2 * dy)
                {
                    points.push_back({px, py});
                    if (dy == 0)
                        break;
                }
            }

            mdx = dx;
            break;
        }
    }
}

void binary_search(point const& center, int radius, std::vector<point>& points)
{
    constexpr auto search = []( const int &radius, const int &squad_radius, int dx, const int &y)
    {
        int l = y, r = y + radius, distance;

        while (l < r)
        {
            int m = l + (r - l) / 2;
            distance = dx * dx + (y - m) * (y - m);
            if (distance > squad_radius)
                r = m - 1;
            else if (distance < squad_radius)
                l = m + 1;
            else
                r = m;
        }

        if (dx * dx + (y - l) * (y - l) > squad_radius)
            --l;

        return l;
    };

    int squad_radius = radius * radius;    
    for (int px = center.x - radius; px <= center.x + radius; ++px)
    {
        int upper_limit = search(radius, squad_radius, px - center.x, center.y);
        for (int py = 2*center.y - upper_limit; py <= upper_limit; ++py)
        {
            points.push_back({px, py});
        }
    }
}

2
@creativecreatorormaybenot np ここで結果を更新できるようにアルゴリズムを調整する場合は、この回答を編集してください。
ティモ

1
@ Mandy007ベンチマークを更新しました。
ティモ

2
for (line = 1; line <= r; line++) {
   dx = (int) sqrt(r * r - line * line);
   for (ix = 1; ix <= dx; ix++) {
       putpixel(x - ix, y + line)
       putpixel(x + ix, y + line)
       putpixel(x - ix, y - line)
       putpixel(x + ix, y - line)
   } 
}

軸でのピクセルの繰り返し生成を回避するには、ループを1から開始し、中心線(ix == 0またはline == 0)を別のループで描画することをお勧めします。

円周上の点を生成するための純粋な整数ブレゼンハムアルゴリズムもあります。


ここで対称性を使用することの真の利益はありません。サークルの4分の1を生成するようにコードを変更しました。
MBo

2

では、最初に、円の内側の正方形を計算します。その式は簡単です。

x² + y² = r²    // circle formula
2h² = r²        // all sides of square are of equal length so x == y, lets define h := x
h = r / sqrt(2) // half side length of the inner square

さて、間のすべての点(-h, -h)(+h, +h)円内に位置します。これが私の意味するイメージです:

1

残りの青い部分は少しトリッキーですが、あまり複雑でもありません。青い円の一番上から始め(x = 0, y = -radius)ます。次に、x++円周境界線を離れるまで(x²+y² < r²もう保持されなくなるまで)右に進みます()。(0、y)と(x、y)の間はすべて円の中にあります。対称性があるため、この8倍に拡張できます。

  • (-x、-y)、(+ x、-y)
  • (-x、+ y)、(+ x、+ y)
  • (-y、-x)、(-y、+ x)
  • (+ y、-x)、(+ y、+ x)

ここで、1行下に移動し(y--)、上記の手順を繰り返します(の最新の値を維持x)。円の中心を各ポイントに追加すれば完了です。

これは視覚化です。アップスケーリングのためにいくつかのアーティファクトがあります。赤い点は、各反復でテストしているものを示しています。

1

以下に完全なコードを示します(opencvを使用して描画します)。

#include <opencv2/opencv.hpp>

constexpr double sqrt2 = 1.41421356237309504880168;

int main()
{
    cv::Point center(200, 200);
    constexpr int radius = 180;

    // create test image
    cv::Mat img(400, 400, CV_8UC3);
    cv::circle(img, center, radius, {180, 0, 0}, cv::FILLED);
    cv::imshow("img", img);
    cv::waitKey();

    // calculate inner rectangle
    int halfSideLen = radius / sqrt2;
    cv::Rect innerRect(center.x - halfSideLen, center.y - halfSideLen, halfSideLen * 2, halfSideLen * 2);
    cv::rectangle(img, innerRect, {0, 180, 0}, cv::FILLED);
    cv::imshow("img", img);
    cv::waitKey();

    // probe the rest
    int x = 0;
    for (int y = radius; y >= halfSideLen; y--)
    {
        for (; x * x + y * y < radius * radius; x++)
        {
            // anything between the following points lies within the circle
            // each pair of points represents a line
            // (-x, -y), (+x, -y)
            // (-x, +y), (+x, +y)
            // (-y, -x), (-y, +x)
            // (+y, -x), (+y, +x)

            // center + {(-X..X) x (-Y..Y)} is inside the circle
            cv::line(img, cv::Point(center.x - x, center.y - y), cv::Point(center.x + x, center.y - y), {180, 180, 0});
            cv::line(img, cv::Point(center.x - x, center.y + y), cv::Point(center.x + x, center.y + y), {180, 180, 0});
            cv::line(img, cv::Point(center.x - y, center.y - x), cv::Point(center.x - y, center.y + x), {180, 180, 0});
            cv::line(img, cv::Point(center.x + y, center.y - x), cv::Point(center.x + y, center.y + x), {180, 180, 0});

            cv::imshow("img", img);
            cv::waitKey(20);
        }
    }

    cv::waitKey();
    return 0;
}

@creativecreatorormaybenot私のバージョンは、中央の正方形の計算が一定の時間であるため(非常に小さな画像を除く)、より速いはずです(トリガー関数を使用しない場合、基本的な数値のみを使用します)。これはまた、あなたがperfomに持っていないことを意味任意の正方形の内部チェックを。残りの画像については、アルゴリズムは基本的に同じです。
ティモ

1
同様にそれに気づきました。これは、アセンブラレベルでのマイクロ最適化に要約されるかもしれません。後でベンチマークをするかもしれません。
ティモ

2

これは、検索の次元を1/4に削減する最適化です。

for (int px = x; px <= x + r; ++px) {
  bool find = false;
  int dx = x - px, dy;
  for (int py = y; !find && py <= y + r; ++py) {
    dy = y - py;
    if (dx * dx + dy * dy <= r * r)) {
      /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
       & (x+x-px+r, y+y-py+r) are part of the circle.*/
    }else{
      find = true; //Avoid increasing on the axis y
    }
  }
}

またはより良い、条件for を回避して2番目の円の反復のパフォーマンスを向上させるif

for (int px = x; px <= x + r; ++px) {
  int dx = x - px, py = y;
  for (; dx * dx + (py-y) * (py-y) <= r * r; ++py) {
    /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
     & (x+x-px+r, y+y-py+r) are part of the circle.*/
  }
}

まあ私は他のオプションは上限のバイナリ検索だと思います:

int binarySearch(int R, int dx, int y){
  int l=y, r=y+R;
  while (l < r) { 
    int m = l + (r - l) / 2;  
    if(dx*dx + (y - m)*(y - m) > R*R) r = m - 1; 
    else if(dx*dx + (y - m)*(y - m) < R*R) l = m + 1; 
    else r = m;
  }
  if(dx*dx + (y - l)*(y - l) > R*R) --l;
  return l;
}

for (int px = x; px <= x + r; ++px) {
  int upperLimit = binarySearch(r, px-x, y);
  for (int py = y; py <= upperLimit; ++py) {
    /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
     & (x+x-px+r, y+y-py+r) are part of the circle.*/
  }
}

バイナリサーチのアイデアは、上限を最適に見つけifforサイクル内の条件と計算を回避することです。このため、現在のポイントと円内の半径との間の距離を作る最大の整数がどれであるかがチェックされます。

PD:私の英語をごめんなさい。


2

コード

@ScottHunterのアイデアに基づいて、次のアルゴリズムを思いつきました。

#include <functional>

// Executes point_callback for every point that is part of the circle
// defined by the center (x, y) and radius r.
void walk_circle(int x, int y, int r,
                 std::function<void(int x, int y)> point_callback) {
  for (int px = x - r; px < x + r; px++)
    point_callback(px, y);
  int mdx = r;
  for (int dy = 1; dy <= r; dy++)
    for (int dx = mdx; dx >= 0; dx--) {
      if (dx * dx + dy * dy > r * r)
        continue;
      for (int px = x - dx; px <= x + dx; px++) {
        point_callback(px, y + dy);
        point_callback(px, y - dy);
      }
      mdx = dx;
      break;
    }
}

アルゴリズムの説明

このアルゴリズムは、1分間のチェックを実行します。具体的には、円の一部である最初の点に到達するまで、各行のみをチェックします。さらに、次の行で以前に識別されたポイントの左側にあるポイントをスキップします。さらに、対称性を使用することにより、行の半分のみ(n/2 + 1/20から開始)がチェックされます。

可視化

これは私が作成したアルゴリズムを視覚化したものです。赤いアウトラインは、以前にチェックされた正方形を示し、黒いピクセルは実際の円を示します(中央の赤いピクセルが中心です)。アルゴリズムはポイント(青でマーク)をチェックし、有効なポイント(緑でマーク)をループします。
ご覧のように、最後の青いピクセルの数はわずかです。つまり、ループの一部である、円の一部ではない点がいくつかあります。さらに、毎回最初の緑のピクセルだけがチェックを必要とし、他のピクセルはループされるだけなので、それらがすぐに表示される理由に注意してください。

ノート

もちろん、軸は簡単に逆にすることができます。

これは、対称性をさらに活用することで最適化できます。つまり、行は列と同じになります(すべての行を通過することは、すべての列を通過することと同じで、左から右、上から下、逆も同様です)万力のヴェラ)そして、中心から行の4分の1だけ下がれば、どの点が円の一部になるかを正確に決定するのに十分でしょう。ただし、これによってもたらされる小さなパフォーマンスバンプは、追加のコードに値するものではないように感じます。
誰かがそれをコード化したい場合は、この回答の編集を提案してください。

コメント付きのコード

#include <functional>

// Executes point_callback for every point that is part of the circle
// defined by the center (x, y) and radius r.
void walk_circle(int x, int y, int r,
                 std::function<void(int x, int y)> point_callback) {
  // Walk through the whole center line as it will always be completely
  // part of the circle.
  for (int px = x - r; px < x + r; px++)
    point_callback(px, y);
  // Define a maximum delta x that shrinks whith every row as the arc
  // is closing.
  int mdx = r;
  // Start directly below the center row to make use of symmetry.
  for (int dy = 1; dy <= r; dy++)
    for (int dx = mdx; dx >= 0; dx--) {
      // Check if the point is part of the circle using Euclidean distance.
      if (dx * dx + dy * dy > r * r)
        continue;

      // If a point in a row left to the center is part of the circle,
      // all points to the right of it until the center are going to be
      // part of the circle as well.
      // Then, we can use horizontal symmetry to move the same distance
      // to the right from the center.
      for (int px = x - dx; px <= x + dx; px++) {
        // Use y - dy and y + dy thanks to vertical symmetry
        point_callback(px, y + dy);
        point_callback(px, y - dy);
      }

      // The next row will never have a point in the circle further left.
      mdx = dx;
      break;
    }
}

JSはおそらく、各円のピクセル座標をオンザフライで「ブルートフォース」で計算するのに十分高速です(おそらく最適化は必要ありません)。しかし、半径を1つだけテストするか、半径をいくつかテストするだけですか?はいの場合、円の内部ポイントを事前に計算して配列に入れ、それらの計算されたポイントを再利用できます。テストしている円の原点にオフセットします。
markE

1
あなたは私のコードの最後の更新を見ることができます私はそれがより良いと思います
Mandy007

2

問題はO(n ^ 2)の固定された複雑さを持ちます。ここで、nは円の半径です。正方形または通常の2D形状と同じ複雑さ

対称性を利用しても、複雑さは同じままであっても、円のピクセル数を減らすことができないという事実を乗り越えることはできません。

したがって、複雑さを無視して最適化を探します。

あなたの質問でabsは、ピクセル(または4番目のピクセル)ごとに少し高すぎると述べています

1行に1回は1ピクセルに1回よりも優れています

行ごとに1平方根に減らすことができます。円の半径256で128の平方根

void circle(int x, int y, int radius) {
    int y1 = y, y2 = y + 1, r = 0, rSqr = radius * radius;
    while (r < radius) {
        int x1 = x, x2 = x + 1, right = x + sqrt(rSqr - r * r) + 1.5;;
        while (x2 < right) {
            pixel(x1, y1);
            pixel(x2, y1);
            pixel(x1--, y2);
            pixel(x2++, y2);
        }
        y1--;
        y2++;
        r++;
    }
}

それをさらに活用するには、sqrtルート計算用のルックアップテーブルを作成します。

すべて整数

別の方法として、平方根をすべての整数演算に置き換えるブレゼンハム線のバリエーションを使用できます。しかし、それは混乱であり、デバイスに浮動小数点ユニットがない場合を除いて、何の利点もありません。

void circle(int x, int y, int radius) {
    int l, yy = 0, xx = radius - 1, dx = 1, dy = 1;
    int err = dx - (radius << 1);
    int l2 = x, y0 = y, r2 = x + 1;
    int l1 = x - xx, r1 = r2 + xx;
    int y2 = y0 - xx, y1 = y0 + 1, y3 = y1 + xx;
    while (xx >= yy) {
        l = l1;
        while (l < r1) {
            pixel(l, y1);
            pixel(l++, y0);
        }
        l = l2;
        while (l < r2) {
            pixel(l, y3);
            pixel(l++, y2);
        }
        err += dy;
        dy += 2;
        y0--;
        yy++;
        y1++;
        l2--;
        r2++;
        if (err > 0) {
            dx += 2;
            err += (-radius << 1) + dx;
            xx--;
            r1--
            l1++
            y3--
            y2++
        }
    }
}

1

円の中に収まる正方形を描くことができ、点が当てはまるかどうかを見つけるのは非常に簡単です。

これは、すべての(4 * r ^ 2)ポイントを検索する代わりに、O(1)時間でほとんどのポイント(2 * r ^ 2)を解決します。

編集:残りのポイントについては、他のすべてのピクセルをループする必要はありません。次の正方形の4辺(北、東、南、西)の寸法[(2r / sqrt(2))、r-(r / sqrt(2))]のサイズの4つの長方形をループする必要があります。内部。つまり、角の四角を探す必要がないということです。完全に対称なので、入力点の絶対値を取得して、その点が座標平面の正の側の半正方形の内側にあるかどうかを検索できます。つまり、4ではなく1回だけループします。

int square_range = r/sqrt(2);
int abs_x = abs(x);
int abs_y = abs(y);

if(abs_x < square_range && abs_y < square_range){
    //point is in
}
else if(abs_x < r && abs_y < r){  // if it falls in the outer square
    // this is the only loop that has to be done
    if(abs_x < abs_y){
        int temp = abs_y;
        abs_y = abs_x;
        abs_x = temp;
    }
    for(int x = r/sqrt(2) ; x < r ; x++){
        for(int y = 0 ; y < r/sqrt(2) ; y++){
             if(x*x + y*y < r*r){
                 //point is in
             }
         }    
    }    
}        

コードの全体的な複雑さはO((rr / sqrt(2))*(r / sqrt(2)))です。これは、内側の正方形と円の外側の境界線の間にある単一の長方形(8方向対称)の半分に対してのみループします。


このアプローチは、円の内側に正方形を描画します。残りのサークルはどうですか?
MBo

正方形の外にある他の点について述べたように、ループで検索されます。
ヒープオーバーフロー

全体的な複雑さは2次式ではO(n^2)なく、O((r-r/sqrt(2))* (r/sqrt(2)))大きなO表記には係数を含めないでください。最高のパワー以外はすべて無視する必要があります。この問題は以下で簡略化できませんO(n^2)
Blindman67

私はビッグオー記法を知っていますが、私のアルゴリズムは、このアルゴリズムは他のO(n ^ 2)に比べて演算が少ないことを述べることでした。
ヒープオーバーフロー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.