สมการสำหรับทดสอบว่าจุดใดอยู่ในวงกลม


309

หากคุณมีวงกลมที่มีศูนย์กลาง(center_x, center_y)และรัศมีradiusคุณจะทดสอบได้อย่างไรว่าจุดใดที่มีพิกัด(x, y)อยู่ภายในวงกลม?


20
คำถามนี้เป็นผู้ไม่เชื่อเรื่องภาษาจริง ๆ ฉันใช้สูตรเดียวกันใน java ดังนั้นการติดแท็กใหม่
Gautam

ดูเหมือนว่าคุณสมมติว่าพิกัดเชิงบวกเท่านั้น โซลูชันด้านล่างไม่ทำงานกับพิกัดที่ลงชื่อ
cjbarth

แก้ปัญหามากที่สุดด้านล่างทำงานด้วยพิกัดบวกและลบ เพียงแค่แก้ไขชิ้นอาหารอันโอชะสำหรับผู้ชมในอนาคตของคำถามนี้
วิลเลียมมอร์ริสัน

ฉันลงคะแนนเพื่อปิดคำถามนี้เป็นนอกหัวข้อเนื่องจากเป็นเรื่องเกี่ยวกับคณิตศาสตร์ของโรงเรียนมัธยมมากกว่าการเขียนโปรแกรม
n คำสรรพนาม 'm

คำตอบ:


481

โดยทั่วไปxและต้องตอบสนองy(x - center_x)^2 + (y - center_y)^2 < radius^2

โปรดทราบว่าจุดที่ตอบสนองสมการข้างต้นกับ<แทนที่โดย==มีการพิจารณาจุดบนวงกลมและจุดที่ตอบสนองสมการข้างต้นกับ<แทนที่ด้วย>ถือว่าเป็นนอกวงกลม


6
มันอาจช่วยให้คนที่มีความคิดเลขน้อยลงได้เห็นการทำงานของสแควร์รูทที่ใช้ในการวัดระยะทางเมื่อเทียบกับรัศมี ฉันรู้ว่ามันไม่เหมาะสม แต่เมื่อคำตอบของคุณถูกจัดรูปแบบเหมือนสมการมากกว่าโค้ดบางทีมันอาจจะสมเหตุสมผลกว่าหรือ เพียงข้อเสนอแนะ
William Morrison

30
นี่เป็นคำอธิบายที่เข้าใจได้ง่ายที่สุดในประโยคง่าย ๆ และสมการที่ใช้ได้ทันที ทำได้ดี.
thgc

นี่เป็นความหวังที่ดีที่ฉันจะได้พบทรัพยากรนี้เร็วขึ้น ค่า x มาจากไหน
Devin Tripp

2
@DevinTripp 'x' เป็นพิกัด x ของจุดที่กำลังทดสอบ
คริส

5
สิ่งนี้อาจเห็นได้ชัด แต่ควรระบุไว้ว่า<=จะพบจุดภายในวงกลมหรือบนขอบของมัน
ไทเลอร์

131

ในทางคณิตศาสตร์ Pythagoras อาจเป็นวิธีที่ง่ายอย่างที่หลายคนได้กล่าวถึงแล้ว

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

การคำนวณมีวิธีที่รวดเร็วกว่า กำหนด:

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

หากมีแนวโน้มที่จะอยู่นอกวงกลมนี้ให้ลองนึกภาพสี่เหลี่ยมจัตุรัสรอบ ๆ มันว่าด้านนั้นแทนเจนต์ของวงกลมนี้:

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

ทีนี้ลองนึกภาพเพชรสี่เหลี่ยมจัตุรัสที่ลากอยู่ในวงกลมนี้เพื่อให้มันยอดเยี่ยมแตะที่วงกลมนี้:

if dx + dy <= R then 
    return true.

ตอนนี้เราได้ครอบคลุมพื้นที่ส่วนใหญ่ของเราและมีเพียงพื้นที่เล็ก ๆ ของวงกลมนี้ที่ยังคงอยู่ในระหว่างสี่เหลี่ยมและเพชรของเราที่จะทดสอบ ที่นี่เรากลับไปใช้ Pythagoras ดังที่กล่าวมา

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

หากจุดหนึ่งน่าจะอยู่ในวงกลมนี้มากกว่าให้เรียงลำดับย้อนกลับของ 3 ขั้นตอนแรก:

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.

วิธีอื่นลองนึกภาพสี่เหลี่ยมจัตุรัสในวงกลมนี้แทนที่จะเป็นเพชร แต่ต้องใช้การทดสอบและการคำนวณเพิ่มเติมเล็กน้อยโดยไม่มีความได้เปรียบในการคำนวณ (สี่เหลี่ยมด้านในและเพชรมีพื้นที่เหมือนกัน):

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

ปรับปรุง:

สำหรับผู้ที่สนใจในประสิทธิภาพฉันใช้วิธีนี้ใน c และรวบรวมด้วย -O3

ฉันได้รับเวลาดำเนินการตาม time ./a.out

ฉันใช้วิธีนี้เป็นวิธีปกติและวิธีจำลองเพื่อกำหนดเวลาเหนือศีรษะ

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

ดังนั้นดูเหมือนว่าวิธีนี้มีประสิทธิภาพมากขึ้นในการใช้งานนี้

// 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
คำตอบนี้ยอดเยี่ยม ฉันไม่เคยตระหนักถึงการเพิ่มประสิทธิภาพบางอย่างที่คุณแนะนำ ทำได้ดี.
วิลเลียมมอร์ริสัน

2
ฉันอยากรู้ว่าคุณได้ทำสิ่งเหล่านี้ให้เหมาะสมหรือไม่? ความรู้สึกของฉันคือว่าหลายเงื่อนไขจะช้ากว่าคณิตศาสตร์และเงื่อนไขหนึ่ง แต่ฉันอาจจะผิด
yoyo

3
@ yoyo ฉันไม่ทำโปรไฟล์ล่วงหน้า - คำถามนี้เกี่ยวกับวิธีการเขียนโปรแกรมภาษาใด ๆ หากมีคนคิดว่านี่อาจปรับปรุงประสิทธิภาพในแอปพลิเคชันของพวกเขาพวกเขาควรจะแสดงให้เห็นว่ามันเร็วขึ้นในสถานการณ์ปกติ
philcolbourn

2
ในฟังก์ชั่นที่inCircleNคุณใช้ ABS ที่ไม่จำเป็น อาจไม่มีความแตกต่าง ABS ระหว่างinCircleและinCircleNจะมีขนาดเล็กลง
tzaloga

1
การถอด ABS ช่วยเพิ่มประสิทธิภาพในการทำงานรอบ แต่ไม่เพียงพอ อย่างไรก็ตามวิธีการของฉันมีอคติต่อคะแนนมีแนวโน้มมากขึ้นนอกวงกลมตั้งแต่ R = 1 ด้วยรัศมีสุ่ม [0..499] จะมีจุดอยู่ภายในวงกลมประมาณ 25% และในวงกลมจะเร็วขึ้น
philcolbourn

74

คุณสามารถใช้ Pythagoras เพื่อวัดระยะทางระหว่างจุดของคุณกับจุดศูนย์กลางและดูว่ามันต่ำกว่ารัศมีหรือไม่:

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

แก้ไข (ปลายหมวกถึงพอล)

ในทางปฏิบัติการยกกำลังสองมักจะถูกกว่าการรับกำลังสองและเนื่องจากเราสนใจเพียงแค่การสั่งซื้อเราจึงสามารถสละการรากที่สอง:

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

นอกจากนี้เจสันยังกล่าวอีกว่า <=ควรแทนที่ด้วย<และขึ้นอยู่กับการใช้งานซึ่งอาจสมเหตุสมผลแม้ว่าฉันเชื่อว่ามันไม่เป็นความจริงในแง่คณิตศาสตร์ที่เข้มงวด. ฉันยืนแก้ไขแล้ว


1
แทนที่ dist <= radius โดย dist <radius เพื่อทดสอบจุดที่อยู่ภายในวงกลม
jason

16
sqrt มีราคาแพง หลีกเลี่ยงถ้าเป็นไปได้ - เปรียบเทียบ x ^ 2 + y ^ y กับ r ^ 2
Paul Tomblin

เจสัน: คำจำกัดความของเราอาจไม่เห็นด้วย แต่สำหรับฉันจุดที่อยู่ในวงกลมนั้นมีความสำคัญที่สุดในวงกลมและฉันค่อนข้างมั่นใจว่าเหมืองของฉันนั้นสอดคล้องกับคำนิยามทางคณิตศาสตร์ที่เป็นทางการ
Konrad Rudolph

3
คำจำกัดความทางคณิตศาสตร์อย่างเป็นทางการของการตกแต่งภายในของวงกลมคือสิ่งที่ฉันให้ไว้ในโพสต์ของฉัน จากวิกิพีเดีย: โดยทั่วไปการตกแต่งภายในของบางสิ่งบางอย่างหมายถึงพื้นที่หรือส่วนภายในของมันยกเว้นผนังหรือขอบเขตรอบนอก en.wikipedia.org/wiki/Interior_(topology)
jason

1
ใน Pascal, Delphi และ FPC อำนาจทั้งสองและ sqrt คือการที่มีราคาแพงและไม่มีอำนาจดำเนินการ EG: หรือ** ^วิธีที่เร็วที่สุดที่จะทำเมื่อคุณเพียงแค่ต้อง x ^ 2 หรือ x ^ 3 คือการทำมัน 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;
}

สิ่งนี้มีประสิทธิภาพมากขึ้นและสามารถอ่านได้ มันหลีกเลี่ยงการดำเนินการรากที่สองราคาแพง ฉันยังเพิ่มการตรวจสอบเพื่อตรวจสอบว่าจุดนั้นอยู่ในขอบเขตสี่เหลี่ยมผืนผ้าของวงกลมหรือไม่

การตรวจสอบรูปสี่เหลี่ยมผืนผ้านั้นไม่จำเป็นยกเว้นในหลาย ๆ จุดหรือหลาย ๆ วงกลม หากคะแนนส่วนใหญ่อยู่ในวงกลมการตรวจสอบสี่เหลี่ยมผืนผ้าที่ล้อมรอบจะทำให้สิ่งต่าง ๆ ช้าลงจริง ๆ !

เช่นเคยให้แน่ใจว่าได้พิจารณากรณีการใช้งานของคุณ


12

คำนวณระยะทาง

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

ที่อยู่ใน C # ... แปลงเพื่อใช้ในหลาม ...


11
คุณสามารถหลีกเลี่ยงการโทร Sqrt ราคาแพงได้สองครั้งโดยเปรียบเทียบ D-squared กับ radius-squared
Paul Tomblin

10

คุณควรตรวจสอบว่าระยะทางจากจุดศูนย์กลางของวงกลมไปยังจุดนั้นเล็กกว่ารัศมีหรือไม่

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

5

ดังกล่าวข้างต้น - ใช้ระยะทางแบบยุคลิด

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

ค้นหาระยะห่างระหว่างศูนย์กลางของวงกลมและจุดที่กำหนด หากระยะห่างระหว่างพวกเขาน้อยกว่ารัศมีจุดนั้นจะอยู่ในวงกลม ถ้าระยะห่างระหว่างพวกเขาเท่ากับรัศมีของวงกลมแล้วจุดนั้นอยู่ในเส้นรอบวงของวงกลม ถ้าระยะทางมากกว่ารัศมีแล้วจุดนั้นอยู่นอกวงกลม

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

สมการดังต่อไปนี้คือการแสดงออกว่าการทดสอบถ้าจุดอยู่ภายในวงกลมที่xPและYPเป็นพิกัดของจุดที่XCและYCมีพิกัดของศูนย์กลางของวงกลมและRคือรัศมีของวงกลมที่กำหนดว่า

ป้อนคำอธิบายรูปภาพที่นี่

หากการแสดงออกข้างต้นเป็นจริงแล้วจุดอยู่ในวงกลม

ด้านล่างคือตัวอย่างการใช้งานใน 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

นี่เป็นวิธีเดียวกับที่Jason Punyon พูดถึงแต่มีตัวอย่างรหัสเทียมและรายละเอียดเพิ่มเติม ฉันเห็นคำตอบของเขาหลังจากเขียนสิ่งนี้ แต่ฉันไม่ต้องการลบของฉันออก

ฉันคิดว่าวิธีที่เข้าใจง่ายที่สุดคือการคำนวณระยะทางระหว่างจุดศูนย์กลางของวงกลมกับจุด ฉันจะใช้สูตรนี้:

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

จากนั้นเพียงแค่เปรียบเทียบผลลัพธ์ของสูตรที่ระยะทาง (คนd) radiusกับ หากระยะทาง ( d) น้อยกว่าหรือเท่ากับรัศมี ( r) จุดนั้นจะอยู่ในวงกลม (บนขอบของวงกลมถ้าdและrเท่ากับ)

นี่คือตัวอย่างของรหัสเทียมที่สามารถแปลงเป็นภาษาโปรแกรมได้อย่างง่ายดาย:

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

ตำแหน่งcircle_xและcircle_yพิกัดกึ่งกลางของวงกลมrคือรัศมีของวงกลมและxและyเป็นพิกัดของจุด


2

คำตอบของฉันใน C # เป็นโซลูชันการตัด & วางที่สมบูรณ์แบบ (ไม่ปรับให้เหมาะสม):

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));
}

การใช้งาน:

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

1

ตามที่ระบุไว้ก่อนหน้านี้เพื่อแสดงว่าจุดอยู่ในวงกลมเราสามารถใช้ต่อไปนี้

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

เพื่อแสดงมันแบบกราฟิกเราสามารถใช้:

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

ฉันใช้รหัสด้านล่างสำหรับผู้เริ่มต้นเช่นฉัน :)

ชั้นสาธารณะ 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

การย้ายเข้าสู่โลกของ 3D ถ้าคุณต้องการตรวจสอบว่าจุด 3D นั้นอยู่ใน Unit Sphere หรือไม่คุณต้องทำสิ่งที่คล้ายกัน สิ่งที่ต้องใช้ในการทำงานในแบบ 2D คือการใช้การดำเนินการแบบเวกเตอร์ 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

ฉันรู้ว่าไม่กี่ปีจากคำตอบที่ได้รับการโหวตดีที่สุด แต่ฉันสามารถลดเวลาในการคำนวณได้ 4

คุณจะต้องคำนวณพิกเซลจาก 1/4 ของวงกลมแล้วคูณด้วย 4

นี่คือวิธีการที่ฉันได้มาถึง:

#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

นี่คือรหัส java อย่างง่ายสำหรับการแก้ปัญหานี้:

และคณิตศาสตร์ที่อยู่เบื้องหลัง: /math/198764/how-to-know-if-a-point-is-inside-a-circle

boolean insideCircle(int[] point, int[] center, int radius) {
    return (float)Math.sqrt((int)Math.pow(point[0]-center[0],2)+(int)Math.pow(point[1]-center[1],2)) <= radius;
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.