37.1 두 점 사이의 거리 구하기

Unit 37. 두 점 사이의 거리 구하기

클래스 문법을 알아보았으니 이번에는 클래스를 활용하여 2차원 평면에서 위치를 표현한 뒤 두 점 사이의 거리를 구해보겠습니다.

37.1 두 점 사이의 거리 구하기

두 점 사이의 거리를 구하기 전에 먼저 클래스로 점을 구현해보겠습니다.

37.1.1  클래스로 점 구현하기

2차원 평면에서 위치를 표현하려면 xy값이 필요하겠죠? 다음과 같이 Point2D 클래스를 구현하고 xy를 속성으로 넣습니다.

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

이제 Point2D 클래스로 점 두 개를 만듭니다.

two_point.py

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
p1 = Point2D(x=30, y=20)    # 점1
p2 = Point2D(x=60, y=50)    # 점2
 
print('p1: {} {}'.format(p1.x, p1.y))    # 30 20
print('p2: {} {}'.format(p2.x, p2.y))    # 60 50

실행 결과

p1: 30 20
p2: 60 50

2차원 평면에서 두 점을 표시해보면 다음과 같은 모양이 됩니다.

그림 37-1 2차원 평면으로 본 두 점

37.1.2  피타고라스의 정리로 두 점의 거리 구하기

여기서 두 점의 거리를 구하려면 어떻게 해야 할까요? 학교에서 배운 피타고라스의 정리를 이용하면 됩니다.

  • 임의의 직각삼각형에서 빗변을 한 변으로 하는 정사각형의 넓이는 다른 두 변을 각각 한 변으로 하는 정사각형의 넓이의 합과 같다.
  • a2 + b2 = c2

그럼 점 p1p2로 직각삼각형을 그려봅니다.

그림 37-2 두 점을 이용한 직각삼각형

피타고라스의 정리에 대입하려면 먼저 선 ab의 길이를 구해야 합니다. 우리는 Point2D 클래스의 인스턴스에 두 점의 좌표 정보가 들어있으므로 인스턴스(변수)를 활용하면 됩니다.

a = p2.x - p1.x    # 선 a의 길이
b = p2.y - p1.y    # 선 b의 길이

ap2x에서 p1x를 빼면 되고, bp2y에서 p1y를 빼면 됩니다.

그다음에 피타고라스의 정리에서 c의 길이를 계산하려면 제곱근을 구해야 합니다.

그럼 √(루트)는 어떻게 구현해야 할까요? 이때는 math 모듈의 sqrt 함수를 사용하면 편리합니다. sqrt는 제곱근을 뜻하는 square root에서 따왔습니다.

  • math.sqrt(값)

n  제곱근을 반환, 값이 음수이면 에러 발생

이제 sqrt 함수까지 사용해서 p1p2의 거리를 구해보겠습니다.

two_point_distance.py

import math
 
class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
p1 = Point2D(x=30, y=20)    # 점1
p2 = Point2D(x=60, y=50)    # 점2
 
a = p2.x - p1.x    # 선 a의 길이
b = p2.y - p1.y    # 선 b의 길이
 
c = math.sqrt((a * a) + (b * b))    # (a * a) + (b * b)의 제곱근을 구함
print(c)    # 42.42640687119285

실행 결과

42.42640687119285

이처럼 sqrt 함수에 값을 넣으면 해당 값의 제곱근을 구해줍니다. 여기서는 a의 제곱과 b의 제곱의 합을 (a * a) + (b * b)처럼 표현했는데 거듭제곱(power)을 구하는 pow 함수를 사용해도 됩니다(math 모듈).

  • math.pow(값, 지수)

n  값을 지수만큼 거듭제곱한 값을 반환

즉, a2를 구하고 싶다면 pow(a, 2)처럼 사용합니다. 앞에서 작성한 코드를 pow 함수로 다시 작성하면 다음과 같은 모양이 되겠죠?

c = math.sqrt(math.pow(a, 2) + math.pow(b, 2))

물론 파이썬의 거듭제곱 연산자 **를 사용해도 됩니다.

c = math.sqrt((a ** 2) + (b ** 2))

만약 선의 위치를 구할 때 p2에서 p1을 빼는 것이 아닌 p1에서 p2를 빼면 어떻게 될까요?

a = p1.x - p2.x    # 선 a의 길이
b = p1.y - p2.y    # 선 b의 길이

30 - 60은 -30이고 20 - 50도 -30입니다. 하지만 걱정하지 않아도 됩니다. a2 + b2 = c2 식에서 ab는 같은 값을 두 번 곱하는데 음수(-)끼리 곱하면 항상 양수(+)가 되므로 부호는 상관하지 않아도 됩니다. 즉, 양수(+) * 양수(+) 또는 음수(-) * 음수(-) 상황밖에 없기 때문이죠.

지금까지 클래스를 사용하여 점을 표현하고 두 점 사이의 거리를 구하는 방법을 배웠는데 피타고라스의 정리를 수학 함수로 푸는 부분이 약간 어려웠습니다. 당장 이해가 되지 않는다면 일단 넘어가도 상관없습니다. 나중에 두 점 사이의 거리를 구해야 할 때 다시 돌아와서 찾아보세요.

참고 | 절댓값 함수

내장 함수 abs 또는 math 모듈의 fabs 함수를 사용하면 양수 또는 음수를 절댓값(absolute value)으로 만들 수 있습니다.

abs(값)

정수는 절댓값을 정수로 반환, 실수는 절댓값을 실수로 반환

math.fabs(값)

절댓값을 실수로 반환

참고 | namedtuple 사용하기

파이썬에서는 각 요소에 이름을 지정해 줄 수 있는 튜플인 namedtuple을 제공합니다(collections 모듈). namedtuple은 자료형 이름과 요소의 이름을 지정하면 클래스를 생성해줍니다. 여기서 자료형 이름은 문자열, 요소의 이름은 문자열 리스트로 넣어줍니다.

클래스 = collections.namedtuple('자료형이름', ['요소이름1', '요소이름2'])

namedtuple로 생성한 클래스는 값을 넣어서 인스턴스를 만들 수 있으며 인스턴스.요소이름 또는 인스턴스[인덱스] 형식으로 요소에 접근할 수 있습니다.

인스턴스 = 클래스(값1, 값2)

인스턴스 = 클래스(요소이름1=값1, 요소이름2=값2)

인스턴스.요소이름1

인스턴스[인덱스]

다음은 namedtuple을 사용하여 점을 표현한 뒤 두 점의 거리를 구합니다.

two_point_namedtuple.py

import math
import collections
 
Point2D = collections.namedtuple('Point2D', ['x', 'y'])    # namedtuple로 점 표현
 
p1 = Point2D(x=30, y=20)    # 점1
p2 = Point2D(x=60, y=50)    # 점2
 
a = p1.x - p2.x    # 선 a의 길이
b = p1.y - p2.y    # 선 b의 길이
 
c = math.sqrt((a * a) + (b * b))
print(c)    # 42.42640687119285