Phương trình kiểm tra nếu một điểm nằm trong một vòng tròn


309

Nếu bạn có một vòng tròn có tâm (center_x, center_y)và bán kính radius, làm thế nào để kiểm tra xem một điểm đã cho có tọa độ (x, y)có nằm trong vòng tròn không?


20
Câu hỏi này thực sự là ngôn ngữ bất khả tri, tôi đang sử dụng cùng một công thức trong java, vì vậy hãy gắn thẻ lại.
Gautam

Có vẻ như bạn chỉ giả định tọa độ tích cực. Các giải pháp dưới đây không hoạt động với tọa độ đã ký.
cjbarth

Hầu hết các giải pháp dưới đây làm việc với tọa độ tích cực và tiêu cực. Chỉ cần sửa lại mẩu tin đó cho người xem trong tương lai của câu hỏi này.
William Morrison

Tôi đang bỏ phiếu để đóng câu hỏi này ngoài chủ đề vì nó liên quan đến toán học ở trường cấp hai hơn là lập trình.
n. 'đại từ' m.

Câu trả lời:


481

Nói chung, xyphải thỏa mãn (x - center_x)^2 + (y - center_y)^2 < radius^2.

Xin lưu ý rằng các điểm thỏa mãn phương trình trên được <thay thế bằng ==được coi là các điểm trên đường tròn và các điểm thỏa mãn phương trình trên được <thay thế bằng >được coi là bên ngoài vòng tròn.


6
Nó có thể giúp một số người ít suy nghĩ toán học thấy hoạt động căn bậc hai được sử dụng để đo khoảng cách so với bán kính. Tôi nhận ra rằng điều đó không tối ưu, nhưng vì câu trả lời của bạn được định dạng giống như một phương trình hơn là mã có lẽ nó có ý nghĩa hơn? Chỉ là một gợi ý.
William Morrison

30
Đây là lời giải thích dễ hiểu nhất được cung cấp chỉ trong một câu đơn giản và một phương trình có thể sử dụng ngay lập tức. Làm tốt.
Junc

đây là mong muốn lớn tôi sẽ tìm thấy tài nguyên này nhanh hơn. Giá trị x đến từ đâu?
Devin Tripp

2
@DevinTripp 'x' là tọa độ x của điểm đang được kiểm tra.
Chris

5
Điều này có thể rõ ràng, nhưng cần phải nói rằng <=sẽ tìm thấy các điểm bên trong vòng tròn hoặc trên cạnh của nó.
Tyler

131

Về mặt toán học, Pythagoras có lẽ là một phương pháp đơn giản như nhiều người đã đề cập.

(x-center_x)^2 + (y - center_y)^2 < radius^2

Tính toán, có những cách nhanh hơn. Định nghĩa:

dx = abs(x-center_x)
dy = abs(y-center_y)
R = radius

Nếu một điểm có nhiều khả năng nằm ngoài vòng tròn này thì hãy tưởng tượng một hình vuông được vẽ xung quanh nó sao cho các cạnh của nó là tiếp tuyến của vòng tròn này:

if dx>R then 
    return false.
if dy>R then 
    return false.

Bây giờ hãy tưởng tượng một viên kim cương vuông được vẽ bên trong vòng tròn này sao cho các đỉnh của nó chạm vào vòng tròn này:

if dx + dy <= R then 
    return true.

Bây giờ chúng tôi đã bao phủ hầu hết không gian của chúng tôi và chỉ còn lại một khu vực nhỏ của vòng tròn này nằm giữa hình vuông và hình thoi của chúng tôi để thử nghiệm. Ở đây chúng tôi trở lại Pythagoras như trên.

if dx^2 + dy^2 <= R^2 then 
    return true
else 
    return false.

Nếu một điểm có nhiều khả năng nằm trong vòng tròn này thì hãy đảo ngược thứ tự 3 bước đầu tiên:

if dx + dy <= R then 
    return true.
if dx > R then 
    return false.
if dy > R 
    then return false.
if dx^2 + dy^2 <= R^2 then 
    return true
else
    return false.

Các phương pháp thay thế tưởng tượng một hình vuông bên trong vòng tròn này thay vì một viên kim cương nhưng điều này đòi hỏi nhiều thử nghiệm và tính toán hơn một chút không có lợi thế tính toán (hình vuông bên trong và hình thoi có các khu vực giống hệt nhau):

k = R/sqrt(2)
if dx <= k and dy <= k then 
    return true.

Cập nhật:

Đối với những người quan tâm đến hiệu suất, tôi đã triển khai phương pháp này trong c và được biên dịch với -O3.

Tôi đã đạt được thời gian thực hiện bởi time ./a.out

Tôi đã thực hiện phương pháp này, một phương pháp bình thường và một phương pháp giả để xác định chi phí thời gian.

Normal: 21.3s This: 19.1s Overhead: 16.5s

Vì vậy, có vẻ như phương pháp này hiệu quả hơn trong việc thực hiện này.

// compile gcc -O3 <filename>.c
// run: time ./a.out

#include <stdio.h>
#include <stdlib.h>

#define TRUE  (0==0)
#define FALSE (0==1)

#define ABS(x) (((x)<0)?(0-(x)):(x))

int xo, yo, R;

int inline inCircle( int x, int y ){  // 19.1, 19.1, 19.1
  int dx = ABS(x-xo);
  if (    dx >  R ) return FALSE;
  int dy = ABS(y-yo);
  if (    dy >  R ) return FALSE;
  if ( dx+dy <= R ) return TRUE;
  return ( dx*dx + dy*dy <= R*R );
}

int inline inCircleN( int x, int y ){  // 21.3, 21.1, 21.5
  int dx = ABS(x-xo);
  int dy = ABS(y-yo);
  return ( dx*dx + dy*dy <= R*R );
}

int inline dummy( int x, int y ){  // 16.6, 16.5, 16.4
  int dx = ABS(x-xo);
  int dy = ABS(y-yo);
  return FALSE;
}

#define N 1000000000

int main(){
  int x, y;
  xo = rand()%1000; yo = rand()%1000; R = 1;
  int n = 0;
  int c;
  for (c=0; c<N; c++){
    x = rand()%1000; y = rand()%1000;
//    if ( inCircle(x,y)  ){
    if ( inCircleN(x,y) ){
//    if ( dummy(x,y) ){
      n++;
    }
  }
  printf( "%d of %d inside circle\n", n, N);
}

5
Câu trả lời này là tuyệt vời. Tôi chưa bao giờ nhận ra một số tối ưu hóa mà bạn đề xuất. Làm tốt.
William Morrison

2
Tôi tò mò muốn biết liệu bạn đã định hình những tối ưu hóa này chưa? Cảm giác ruột của tôi là nhiều điều kiện sẽ chậm hơn một số môn toán và một điều kiện, nhưng tôi có thể sai.
yoyo

3
@yoyo, tôi đã tạo thành không có hồ sơ - câu hỏi này là về một phương pháp cho bất kỳ ngôn ngữ lập trình nào. Nếu ai đó nghĩ rằng điều này có thể cải thiện hiệu suất trong ứng dụng của họ thì họ nên, như bạn đề xuất, chứng minh rằng nó nhanh hơn trong các tình huống thông thường.
philcolbourn

2
Trong chức năng inCircleNbạn đang sử dụng ABS không cần thiết. Có lẽ không có sự khác biệt giữa ABS inCircleinCircleNsẽ nhỏ hơn.
tzaloga

1
Loại bỏ ABS sẽ cải thiện hiệu suất inCircleN nhưng không đủ. Tuy nhiên, phương pháp của tôi thiên về các điểm nhiều khả năng nằm ngoài vòng tròn vì R = 1. Với bán kính ngẫu nhiên [0..499], khoảng 25% điểm nằm trong vòng tròn và inCircleN nhanh hơn.
philcolbourn

74

Bạn có thể sử dụng Pythagoras để đo khoảng cách giữa điểm của bạn và trung tâm và xem liệu nó có thấp hơn bán kính không:

def in_circle(center_x, center_y, radius, x, y):
    dist = math.sqrt((center_x - x) ** 2 + (center_y - y) ** 2)
    return dist <= radius

EDIT (mũ cho Paul)

Trong thực tế, bình phương thường rẻ hơn nhiều so với lấy căn bậc hai và vì chúng ta chỉ quan tâm đến việc đặt hàng, nên tất nhiên chúng ta có thể từ bỏ việc lấy căn bậc hai:

def in_circle(center_x, center_y, radius, x, y):
    square_dist = (center_x - x) ** 2 + (center_y - y) ** 2
    return square_dist <= radius ** 2

Ngoài ra, Jason lưu ý rằng <=nên được thay thế bởi <và tùy thuộc vào cách sử dụng, điều này thực sự có thể có ý nghĩamặc dù tôi tin rằng nó không đúng theo nghĩa toán học nghiêm ngặt. Tôi đứng sửa.


1
Thay dist <= radius bằng dist <radius để kiểm tra điểm nằm trong vòng tròn.
jason

16
sqrt là đắt tiền. Tránh nó nếu có thể - so sánh x ^ 2 + y ^ y với r ^ 2.
Paul Tomblin

Jason: định nghĩa của chúng tôi có thể không đồng ý nhưng đối với tôi, một điểm nằm trên chu vi của vòng tròn rõ ràng nhất cũng nằm trong vòng tròn và tôi khá chắc chắn rằng tôi đồng ý với định nghĩa toán học chính thức.
Konrad Rudolph

3
Định nghĩa toán học chính thức về phần bên trong của một vòng tròn là cái mà tôi đã đưa ra trong bài viết của mình. Từ Wikipedia: Nói chung, phần bên trong của một cái gì đó đề cập đến không gian hoặc một phần bên trong của nó, không bao gồm bất kỳ loại tường hoặc ranh giới xung quanh bên ngoài của nó. vi.wikipedia.org/wiki/Interior_(topology)
jason

1
Trong pascal, delphi và FPC, cả sức mạnh và sqrt đều đắt đỏ và không có nhà khai thác năng lượng EG: **hay ^. Cách nhanh nhất để làm điều đó khi bạn chỉ cần x ^ 2 hoặc x ^ 3 là làm "thủ công" : x*x.
JHolta

37
boolean isInRectangle(double centerX, double centerY, double radius, 
    double x, double y)
{
        return x >= centerX - radius && x <= centerX + radius && 
            y >= centerY - radius && y <= centerY + radius;
}    

//test if coordinate (x, y) is within a radius from coordinate (center_x, center_y)
public boolean isPointInCircle(double centerX, double centerY, 
    double radius, double x, double y)
{
    if(isInRectangle(centerX, centerY, radius, x, y))
    {
        double dx = centerX - x;
        double dy = centerY - y;
        dx *= dx;
        dy *= dy;
        double distanceSquared = dx + dy;
        double radiusSquared = radius * radius;
        return distanceSquared <= radiusSquared;
    }
    return false;
}

Điều này là hiệu quả hơn, và có thể đọc được. Nó tránh các hoạt động căn bậc hai tốn kém. Tôi cũng đã thêm một kiểm tra để xác định xem điểm có nằm trong hình chữ nhật giới hạn của hình tròn không.

Kiểm tra hình chữ nhật là không cần thiết ngoại trừ với nhiều điểm hoặc nhiều vòng tròn. Nếu hầu hết các điểm nằm trong vòng tròn, kiểm tra hình chữ nhật giới hạn sẽ thực sự làm mọi thứ chậm hơn!

Như mọi khi, hãy chắc chắn xem xét trường hợp sử dụng của bạn.


12

Tính khoảng cách

D = Math.Sqrt(Math.Pow(center_x - x, 2) + Math.Pow(center_y - y, 2))
return D <= radius

đó là trong C # ... chuyển đổi để sử dụng trong python ...


11
Bạn có thể tránh hai cuộc gọi Sqrt đắt tiền bằng cách so sánh bình phương D với bình phương bán kính.
Paul Tomblin

10

Bạn nên kiểm tra xem khoảng cách từ tâm của vòng tròn đến điểm có nhỏ hơn bán kính hay không

if (x-center_x)**2 + (y-center_y)**2 <= radius**2:
    # inside circle

5

Như đã nói ở trên - sử dụng khoảng cách Euclide.

from math import hypot

def in_radius(c_x, c_y, r, x, y):
    return math.hypot(c_x-x, c_y-y) <= r

4

Tìm khoảng cách giữa tâm của đường tròn và các điểm đã cho. Nếu khoảng cách giữa chúng nhỏ hơn bán kính thì điểm nằm trong vòng tròn. nếu khoảng cách giữa chúng bằng bán kính của vòng tròn thì điểm nằm trên chu vi của vòng tròn. nếu khoảng cách lớn hơn bán kính thì điểm nằm ngoài đường tròn.

int d = r^2 - (center_x-x)^2 + (center_y-y)^2;

if(d>0)
  print("inside");
else if(d==0)
  print("on the circumference");
else
  print("outside");

4

Phương trình dưới đây là một biểu thức kiểm tra nếu một điểm nằm trong một đường tròn đã cho trong đó xP & yP là tọa độ của điểm, xC & yC là tọa độ của tâm của đường tròn và R là bán kính của đường tròn đã cho.

nhập mô tả hình ảnh ở đây

Nếu biểu thức trên là đúng thì điểm nằm trong vòng tròn.

Dưới đây là một triển khai mẫu trong C #:

    public static bool IsWithinCircle(PointF pC, Point pP, Single fRadius){
        return Distance(pC, pP) <= fRadius;
    }

    public static Single Distance(PointF p1, PointF p2){
        Single dX = p1.X - p2.X;
        Single dY = p1.Y - p2.Y;
        Single multi = dX * dX + dY * dY;
        Single dist = (Single)Math.Round((Single)Math.Sqrt(multi), 3);

        return (Single)dist;
    }

2

Đây là giải pháp tương tự như Jason Punyon đã đề cập , nhưng nó chứa một ví dụ mã giả và một số chi tiết khác. Tôi đã thấy câu trả lời của anh ấy sau khi viết bài này, nhưng tôi không muốn xóa câu trả lời của tôi.

Tôi nghĩ rằng cách dễ hiểu nhất là trước tiên hãy tính khoảng cách giữa tâm của vòng tròn và điểm. Tôi sẽ sử dụng công thức này:

d = sqrt((circle_x - x)^2 + (circle_y - y)^2)

Sau đó, chỉ cần so sánh kết quả của công thức đó, khoảng cách ( d), với radius. Nếu khoảng cách ( d) nhỏ hơn hoặc bằng bán kính ( r), điểm nằm trong vòng tròn (trên cạnh của vòng tròn nếu drbằng nhau).

Dưới đây là một ví dụ mã giả có thể dễ dàng chuyển đổi sang bất kỳ ngôn ngữ lập trình nào:

function is_in_circle(circle_x, circle_y, r, x, y)
{
    d = sqrt((circle_x - x)^2 + (circle_y - y)^2);
    return d <= r;
}

Trong đó circle_xcircle_ylà tọa độ trung tâm của vòng tròn, rlà bán kính của vòng tròn xylà tọa độ của điểm.


2

Câu trả lời của tôi trong C # là giải pháp cắt & dán hoàn chỉnh (không được tối ưu hóa):

public static bool PointIsWithinCircle(double circleRadius, double circleCenterPointX, double circleCenterPointY, double pointToCheckX, double pointToCheckY)
{
    return (Math.Pow(pointToCheckX - circleCenterPointX, 2) + Math.Pow(pointToCheckY - circleCenterPointY, 2)) < (Math.Pow(circleRadius, 2));
}

Sử dụng:

if (!PointIsWithinCircle(3, 3, 3, .5, .5)) { }

1

Như đã nêu trước đây, để hiển thị nếu điểm nằm trong vòng tròn, chúng ta có thể sử dụng như sau

if ((x-center_x)^2 + (y - center_y)^2 < radius^2) {
    in.circle <- "True"
} else {
    in.circle <- "False"
}

Để biểu thị nó bằng đồ họa, chúng ta có thể sử dụng:

plot(x, y, asp = 1, xlim = c(-1, 1), ylim = c(-1, 1), col = ifelse((x-center_x)^2 + (y - center_y)^2 < radius^2,'green','red'))
draw.circle(0, 0, 1, nv = 1000, border = NULL, col = NA, lty = 1, lwd = 1)

0

Tôi đã sử dụng mã dưới đây cho người mới bắt đầu như tôi :).

lớp học công cộng incirkel {

public static void main(String[] args) {
    int x; 
    int y; 
    int middelx; 
    int middely; 
    int straal; {

// Adjust the coordinates of x and y 
x = -1;
y = -2;

// Adjust the coordinates of the circle
middelx = 9; 
middely = 9;
straal =  10;

{
    //When x,y is within the circle the message below will be printed
    if ((((middelx - x) * (middelx - x)) 
                    + ((middely - y) * (middely - y))) 
                    < (straal * straal)) {
                        System.out.println("coordinaten x,y vallen binnen cirkel");
    //When x,y is NOT within the circle the error message below will be printed
    } else {
        System.err.println("x,y coordinaten vallen helaas buiten de cirkel");
    } 
}



    }
}}

0

Di chuyển vào thế giới 3D nếu bạn muốn kiểm tra xem điểm 3D có trong Đơn vị hình cầu không, cuối cùng bạn sẽ làm điều gì đó tương tự. Tất cả những gì cần thiết để làm việc trong 2D là sử dụng các hoạt động vectơ 2D.

    public static bool Intersects(Vector3 point, Vector3 center, float radius)
    {
        Vector3 displacementToCenter = point - center;

        float radiusSqr = radius * radius;

        bool intersects = displacementToCenter.magnitude < radiusSqr;

        return intersects;
    }

0

Tôi biết rằng vài năm sau câu trả lời được bình chọn tốt nhất, nhưng tôi đã cố gắng giảm thời gian tính toán xuống 4.

Bạn chỉ cần tính toán các pixel từ 1/4 vòng tròn, sau đó nhân với 4.

Đây là giải pháp tôi đạt được:

#include <stdio.h>
#include <stdlib.h>
#include <time.h> 

int x, y, r;
int mx, c, t;
int dx, dy;
int p;

int main() {
    for (r = 1; r < 128; r++){

        clock_t t; 
        t = clock();

        p = calculatePixels(r);

        t = clock() - t; 
        double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds 

        printf( "%d of pixels inside circle with radius %d, took %f seconds to execute \n", p, r, time_taken);
    }
}

int calculatePixels(int r){
    mx = 2 * r;
    c = (mx+1)*(mx+1);
    t = r * r;
    int a = 0;
    for (x = 0; x < r; x++){
      for (y = 0; y < r; y++){
          dx = x-r;
          dy = y-r;
          if ((dx*dx + dy*dy) > t)
              a++;
          else 
              y = r;
      }
    }
    return (c - (a * 4));
}


0

PHP

if ((($x - $center_x) ** 2 + ($y - $center_y) ** 2) <=  $radius **2) {
    return true; // Inside
} else {
    return false; // Outside
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.