기울기와 한점이 주어졌을 때 동일한 거리만큼 떨어진 두 점 구하기.

나빌레라의 이미지

제목을 줄이려다가 보니 제목이 좀 모호한데요.. 다시 정리하자면

기울기가 M이고 임의의 점 P(PX, PY)가 주어질때 P와 일직선상에 있으면서 거리가 T인 점(두개겠죠?)을 구하는 코드

를 작성하려 합니다. 코드를 작성하기 앞서 10년도 더 전에 배웠던 점과 직선의 방정식등을 구글링(이런걸 구글링 하게 될 줄이야..-_-) 해서 공식과 의사 코드를 작성해 봤습니다.

1. 우선 기울기가 M이고 임의의 점 P(PX, PY)를 지나는 직선의 방정식
직선의 방정식을 y=ax + b 라고 하면,
PY = M * PX + b
b = PY - (M * PX)
∴ y = Mx + (PY - (M * PX)) ........ (가)

2. 이어서 점과 직선사이의 거리
어떤 점 N(x,y)와 P(PX, PY) 사이의 거리를 T라 하면
T = √(PX-x)² + (PY-y)²
T² = (PX-x)² + (PY-y)² ............(나)

3. N은 (가)의 직선에 속하는 점이다. 그래서 (나)식을 풀면
T² = PX² - 2PXx + x² + PY² - 2PYy + y² (대문자가 연이어 있는것은 곱셈이 아니라 하나의 상수입니다.)
(가)식의 y를 풀어진 (나)에 대입하면..

T² = PX² - 2PXx + x² + PY² - 2PY * (Mx + (PY - (M * PX))) + (Mx + (PY - (M * PX)))²

알아 보기 쉽게 상수를 정리해 보겠습니다.

a = T²
b = PX
c = PY
d = PY - (M * PX)

그러면

a² = b² - 2bx + x² + c² - 2c * (Mx + d) + (Mx + d)²

a² = b² - 2bx + x² + c² - 2cMx - 2cd + (Mx)² + 2dMx + d²

위 식에서 우변을 0으로 만들면..

(M²+1)x² + (2dM - 2b - 2cM)x + (b² + c² - 2cd + d² - a²) = 0

이렇게 해서 Ax² + Bx + C = 0 의 형태를 만들고,
근의 공식인

x = -B ± √B² - 4AC / 2A

에 넣어 x를 구하고 이 x를 (가)식에 넣어 y를 구한다.

이게 제가 생각한 방식입니다.

코드로 구현 할때는

M = 기울기
PX = 주어진 점의 X좌표
PY = 주어진 점의 Y좌표
T = 주어진 점으로 부터 떨어진 거리
 
A = (M*M) + 1;
B = (2 * (PY - (M * PX)) * M) - (2 * PX) - (2 * PY * M);
C = (PX * PX) + (PY * PY) - (2 * PY * (PY - (M * PX))) + ((PY - (M * PX)) * (PY - (M * PX))) - (T * T);
 
D = (B * B) - 4 * (A * C);
 
if D > 0
    x1 = ((-1 * B) + sqrt(D)) / (2 * A);
    x2 = ((-1 * B) - sqrt(D)) / (2 * A);
 
    y1 = (M * x1) + (PY - (M * PX));
    y2 = (M * x2) + (PY - (M * PX));
else if D == 0
    // 전제한 조건으로 부터 근이 한개만 나올 수는 없음
    return -1;
else D < 0
    return -1;

이렇게 되겠죠...

테스트 프로그램을 작성해서 몇 가지 넣어 본 결과 잘되는 것 같긴 합니다만...

이렇게 수학적 방법을 동원해서 알고리즘을 작성하는 방법 밖에는 없는 것인지..
다른 방법은 없는 것인지..
다른 방법은 없다면 제가 작성한 저 방법이 맞는 것인지..

혹시 심심한 분들..
제가 생각한것 보다 더 깔끔하고 뭔가 아티스틱한 알고리즘 없을 랑가요...

댓글

neogeo의 이미지

sin , cos 이 연산 cost 가 싸다면

기울기 가 이루는 각 X = inverse of tangent '기울기' 를 이용해 X 를 구하고, ( 단 기울기가 0 이거나 무한대가 아니어야겠죠. 0 인경우는 X 축 쪽으로만 더하거나 빼면 끝이므로 )

거리가 D 라고 한다면,

원래 점 P(px,py) 이고 구하는 점 P1 은 (px + D cos X , py + D sin X ) , P2 는 ( px - D cos X , py - D six X )

입니다.

일반적으로 sin , cos 는 cost 가 크므로 어느쪽이 빠른지는 잘 생각해보셔서 사용하세요. ( 하드웨어에 따라 다르겠죠~ )

Neogeo - Future is Now.

Neogeo - Future is Now.

jick의 이미지

dx = T / sqrt(1 + M*M)
dy = T * M / sqrt(1 + M*M)

원하는 점 1 = (PX + dx, PY + dy)
원하는 점 2 = (PX - dx, PY - dy)

왜 그런지는 좌표평면에 이 점들을 찍어본다음에 잘 생각해 보세요. 피타고라스의 정리만 있으면 됩니다.

krisna의 이미지

다른 분들이 저렇게 답을 하니까 마치 위 공식은 다른 결과가 나오는 것처럼 되어 버렸는데요.
위 식을 정리하면 동일한 결과가 나옵니다.
식이 복잡해진 걸 정리하지 않아서 그럽니다.

어떤 방식으로 풀어도 결과는 같아야 합니다.

직선의 식은 아래와 같습니다.

y = m * x + c = m * x + Py - m * Px

위 식을 아래 식에 대입하면

T² = (x - Px)² + (y - Py)²
= (x - Px)² + (m * x + Py - m * Px - Py)²
= (x - Px)² + (m * x - m * Px)²
= (x - Px)² + m² * (x - Px)²
= (1 + m²) * (x - Px)²

가 되어

                     T² 
(x - Px)² = --------
                 (1 + m²)
 
                    T
x = Px ± -----------
              sqrt(1 + m²)

입니다.
neogeo의 이미지

어차피 삼각함수 자체가 거기서 나온것이므로 마찬가지는 맞습니다만,

특정 하드웨어의 경우 sin , cos 가 특별히 빠른케이스가 있습니다. 그래서 상황에 따라 같은 공식이지만 sin , cos 를 이용하면 전혀 다른 경우가 나올 수 있습니다.

심지어 일반 PC라도 cutomized math lib 을 쓰는 케이스에도 그냥 계산보다 sin , cos , arctan 이 빠를때도 있습니다.

그래서 하드웨어나 상황에 따라 sqrt 를 쓰거나 혹은 sin cos 를 사용하거나 하는건 테스트해보고 사용해보시라고 위에 적은 것 입니다.

Neogeo - Future is Now.

Neogeo - Future is Now.

pamisu1의 이미지

직선과 엑스축이 이루는 각에 대한 탄젠트 값이 곧 기울기 입니다.

그리고 탄젠트 값은 사인값/코사인값 으로 나타낼 수 있습니다.
또한 사인값 제곱한 것과 코사인값 제곱한 것을 더하면 1이 되지요.

기울기 m= sin값/cos값 이므로 sin값 = m*cos값 입니다.

그러므로 (sin값)^ + (cos값)^ = m^(cos값)^ + (cos값)^ = (m^+1)*(cos값)^ = 1 이 됩니다.

따라서 cos값은 1/(m^+1) 에 루트를 씌운 겁니다. 이렇게 구한 걸 K 라고 하겠습니다.
sin값은 이렇게 구한 K 에 기울기 m 을 곱한 mK 가 되겠죠.

기울기가 m 인 직선상의 한 점 P(a,b)에서 거리 T 만큼 떨어진 같은 직선상의 점 Q를 구해보겠습니다.

Q1= (a+TK,b+mTK)
Q2= (a-TK,b-mTK)

아니면 원점을 지나는 기울기 m 인 직선 y=mx 와 원점이 중심인 원 x^+y^= T^ 이 만나는 점을 구한 후에 엑스축으로 a, 와이축으로 b 만큼 이동시켜도 됩니다.

ps.
루트 씌우는 거나 제곱표시같은 수식표현을 할 줄 몰라서 글로 쓰기가 참으로 힘드네요.

익명 사용자의 이미지

와우 많은 도움이 되었습니다. 감사합니다^^

esrevinu의 이미지

(px,py)를 중심으로 한 반지름 T인 원과 (px,py)를 지나고 기울기 M인 직선의 교차점;
다음 연립방정식을 푸세요.
(x-px)^2 + (y-py)^2 = T^2,
(y-py) = M*(x-px)

snowall의 이미지

neogeo님이 쓴 방법을 짧게 쓰면,,,

(T,0)을 M의 기울기를 갖도록 회전변환하고, -(px,py)만큼 평행이동합니다.

(x,y)=(T*cos(atan(M)+px,T*sin(atan(M)+py)

-T에 대해서도 똑같이 할 수 있겠군요.

삼각함수와 삼각함수 근사식의 cost를 비교해서 더 작은 cost가 드는 걸로 계산하면 되겠죠.

pamisu1 님이 쓰신 글도 참고해 보면 sin(atan(M))=M*cos(atan(M))

계산을 돌리는 것보다 메모리에 넣었다 읽어오는게 cost가 더 싸고, 삼각함수 계산보다 그냥 이항연산이 더 Cost가 싸다면 코드는 좀 길지만

ArcTangentOfM = atan(M)
Cosine = cos(ArcTangentOfM)
(x1,y1) = (T*Cosine+px, T*M*Cosine+py)
(x2,y2) = (-T*Cosine+px, -T*M*Cosine+py)

정도가 되겠네요
--------------------------
snowall의 블로그입니다.
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

snowall의 이미지

깔끔하지도 않고 아티스틱하지도 않은...나름 몬테카를로 방법
그냥 대강의 코드 형태만 짜 봤습니다.

error1 = 0.01 // 거리 오차값
error2 = 0.01 // 기울기 오차값
(x,y)=(px,py) // 처음엔 거기서 출발합니다
 
while(1){  // 될때까지 무한반복
 (ran_x,ran_y)=(rand(),rand())    //적당히 랜덤한 좌표 하나를 만듭니다
 (x_backup, y_backup)=(x,y) // 일단 백업해두고
 (x,y)=(x,y)+(ran_x,ran_y) //그쪽으로 갑니다
 if abs(distance((x,y),(px,py)) - T) < abs(distance((x_backup,y_backup),(px,py)) - T) 
      || abs(tangent((x,y),(px,py)) - M) < abs(tangent((x_backup,y_backup),(px,py)) - M) 
      then (x,y)=(x_backup,y_backup) // 비교해서 더 조건이 나빠졌으면 원래대로 돌아감.
 if abs(distance((x,y),(px,py)) - T) < error1 && abs(tangent((x,y),(px,py)) - M) < error2 then { //비교해서 기준치를 만족하면 탈출
  print((x,y))
  exit
 }
}
 
rand() //난수를 발생시킵니다. 적당한 함수 정의를 통하여 0부터 T사이의 난수만 발생시키기로 합니다.
abs() //입력받은 실수의 절대값을 반환합니다
distance() //두 점 사이의 거리를 실수값으로 알려줍니다
tangent() //두 점이 만드는 직선의 기울기를 알려줍니다
print() //입력받은 점을 출력합니다

--------------------------
snowall의 블로그입니다.
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

아다치의 이미지

이걸 몬테카를로 방법이라고 하기는 좀...
그냥 눈감고 찍기네요.

snowall의 이미지

앗, 들켰군요 -_-;

--------------------------
snowall의 블로그입니다.
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

blmarket의 이미지

이걸 국소탐색이라 봐야 하는건지 라스베가스 알고리즘이라고 봐야하는지 아시는 분 안계십니까?

Asche Zu Asche

sangwoo의 이미지

눈감고 찍는게 Monte Carlo simulation의 기본 철학 아닌가요? :-)
물론 눈 뜨고 찍으면 더 좋을 때가 많습니다만.. (importance sampling)
----
Let's shut up and code.

----
Let's shut up and code.

자진삭제의 이미지

.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.