본문 바로가기

Computer Science/Baekjoon

[Baekjoon] 10101번 삼각형 외우기 B4 <Short>

728x90

10101번 - 삼각형 외우기

1. 문제

창영이는 삼각형의 종류를 잘 구분하지 못한다. 따라서 프로그램을 이용해 이를 외우려고 한다.

삼각형의 세 각을 입력받은 다음,

  • 세 각의 크기가 모두 60이면, Equilateral
  • 세 각의 합이 180이고, 두 각이 같은 경우에는 Isosceles
  • 세 각의 합이 180이고, 같은 각이 없는 경우에는 Scalene
  • 세 각의 합이 180이 아닌 경우에는 Error

를 출력하는 프로그램을 작성하시오.

  • Input

총 3개의 줄에 걸쳐 삼각형의 각의 크기가 주어진다. 모든 정수는 0보다 크고, 180보다 작다.

  • Output

문제의 설명에 따라 Equilateral, Isosceles, Scalene, Error 중 하나를 출력한다.

2. 사용 알고리즘

  • 수학 (Math)

3. 풀이

해당 풀이는 가능한 한 코드의 길이를 줄이고자 하는 숏코딩 방식으로 구현되었다.

3.1. 삼각형의 종류

문제를 풀기에 앞서 우리는 2차원 평면상에 존재할 수 있는 삼각형의 종류에 대해 확인해볼 필요가 있다.

변의 길이에 따른 분류

  • 정삼각형 (Equilateral) : 세 변의 크기가 같은 삼각형
  • 이등변삼각형 (Isosceles) : 두 변의 크기가 같은 삼각형
  • 부등변삼각형 (Scalene) : 세 변의 길이가 모두 다른 삼각형

각의 크기에 따른 분류

  • 직각삼각형 (Right) : 한 개의 직각이 존재하는 삼각형
  • 예각삼각형 (Acute) : 세 각이 모두 예각인 삼각형
  • 둔각삼각형 (Obtuse) : 한 개의 둔각이 존재하는 삼각형

위 문제에서는 Equilateral, Isosceles, Scalene 의 세 가지 종류를 얘기하였고, 이를 번역하면 각각 정삼각형, 이등변삼각형, 부등변삼각형 이라 할 수 있다.


따라서 입력된 삼각형을 변의 길이에 따라 분류해야 한다고 할 수 있다.

3.2. 각의 크기 입력?

하지만 문제를 살펴보면 변의 길이가 아닌 각의 크기가 입력된다. 이는 정삼각형, 이등변삼각형, 부등변삼각형 이 변의 길이에 따라 분류된 것은 자명하지만, 삼각형의 성질 자체가 각의 크기에 따라서도 동일하게 적용된다.


  • 정삼각형세 변의 길이가 같은 삼각형 으로써 정의되지만 자연적으로 세 각의 크기가 $60 ^{\circ}$ 로 같게되는 성질을 지닌다.
  • 이등변삼각형 역시 두 변의 길이가 같은 삼각형 으로써 각의 크기가 같은 두 밑각이 존재하게 된다.
  • 이 두 경우를 제외한 부등변삼각형 은 자연스럽게 모든 각의 크기가 서로 다르게 되는 것이다.


다만 주의할 점은 삼각형의 정보로써 세 변이 주어지면 그 삼각형은 하나로 특정지을 수 있지만, 각의 크기는 그렇지 못하다는 점에 있다.


가령, 세 변의 길이가 $1$, $2$, $\sqrt{3}$ 인 삼각형은 반드시 하나 존재하지만, 세 각도의 크기가 $30 ^{\circ}$, $60 ^{\circ}$, $90 ^{\circ}$ 인 삼각형은 무수히 많이 존재할 수 있다. 세 변의 길이의 비가 $1 : 2 : \sqrt{3}$ 이기만 하면되기 때문에 각 변의 길이가 $\sqrt{3}$, $2\sqrt{3}$, $3$ 인 삼각형도 해당 삼각형 범주에 포함된다.


하지만 해당 무수히 많은 삼각형은 닮음이다. 세 각의 크기가 같은 삼각형은 무수히 많지만 모두 정삼각형 이며, 두 각의 크기가 같은 삼각형과 각의 크기가 모두 다른 삼각형 역시 각각 모두 이등변삼각형부등변삼각형 이 된다.


더불어 세 각의 크기의 합이 $180 ^{\circ}$ 가 아닐 경우, 평면상에서 삼각형으로 존재할 수 없게된다.

3.3. 사용 개념

해당 풀이에서 적극적으로 사용할 개념은 집합논리곱의 성질이다.

3.3.1 집합

집합은 기본적으로 원소의 중복을 허용하지 않으므로 리스트의 중복을 제거하는 역할로 사용되기도 한다.

print(list(set([1, 2, 1, 3, 4, 4])))

실행결과

[1, 2, 3, 4]

3.3.2. 논리곱의 성질

Python 언어에서 논리곱은 다음과 같이 작동한다.

A and B 에 대하여

  • A이면 A and B == B
  • A거짓이면 A and B == A

이 연산의 핵심은 무조건적 TrueFalse 의 Boolean 값이 아닌 A, B 자체가 반환된다는 것이다.


다음 예시를 살펴보자.

print(True and 1)   # 1
print(True and 0)   # 0 
print(False and 1)  # False
print(False and 0)  # False

실행결과

1
0 
False
False

위 예시에서는 bool 값과 int 값의 논리연산을 수행한다.

  • True and 1 : A 값이 이므로 결과값은 B 값의 1
  • True and 0 : A 값이 이므로 결과값은 B 값의 0
  • False and 1 : A 값이 거짓이므로 결과값은 A 값의 False
  • False and 0 : A 값이 거짓이므로 결과값은 A 값의 False

A 값이 거짓이면 더 볼 것도 없이 A 이고, A 값이 참일 경우 B 값의 참/거짓 여부를 판단한 후 결과값을 결정하는 연산 조차 생략하고, 결과값이 그냥 B 값 그 자체로써 결정된다.


즉, 다음 코드와 동일한 역할을 수행한다.

A if not A else B

따라서 이를 이용하면 삼항연산자가 사용된 특정한 경우에 한해 논리곱으로써 나타낼 수 있다.

해당 문제에 대한 풀이로 이 개념을 사용하고자 하는 것이다.


Python 의 논리 연산에 대한 자세한 설명은 여기를 통해 확인할 수 있다.


4. 구현

이제 실제로 구현해보자.

4.1. 값 입력

먼저 세 줄에 걸쳐 입력되는 각의 크기에 해당하는 숫자를 리스트에 담아야 한다.

가장 단순하게는 다음과 같이 구현할 수 있다.

a=[int(input())for _ in range(3)]

하지만 무려 4 B 를 줄일 수 있는 방법이 존재한다.

a=[int(input())for _ in'   ']

이를 통해 변수 a 에는 세 각의 크기 리스트를 담을 수 있다.

4.2. 출력값 리스트

출력값들이 담긴 리스트는 다음과 같이 나타낸다.

['Error','Equilateral','Isosceles','Scalene']

4.3. 입력값에 따른 색인 설정

4.2. 에서 만든 리스트에 색인을 통해 접근하고자 한다. 리스트의 길이가 $4$ 이므로 색인은 $0$, $1$, $2$, $3$ 의 경우가 가능하다.

4.3.1. 180==sum(a)

리스트 a 에 대하여 모든 원소의 합 (sum(a)) 이 $180$ 이면 삼각형이고, 그렇지 않으면 삼각형이 아니다.

4.3.2. len(set(a))

set(a) 은 리스트 a 의 중복을 제거하므로 그것의 길이 len(set(a)) 는 곧 삼각형을 분류하는 역할을 한다.

a = [60, 60, 60] # Equilateral
b = [45, 45, 90] # Isosceles
c = [30, 60, 90] # Scalene
print(len(set(a)))
print(len(set(b)))
print(len(set(c)))

실행결과

1
2
3

정삼각형 은 $1$, 이등변삼각형 은 $2$, 부등변삼각형 은 $3$ 을 도출한다.


도출된 값은 출력값 리스트에서 정확히 해당 값의 위치를 나타낸다.

4.3.3. 삼항연산자?

위에서 정삼각형 은 $1$, 이등변삼각형 은 $2$, 부등변삼각형 은 $3$ 을 도출하는 식 len(set(a)) 을 구현하였다. 여기에 더불어 180!=sum(a) 일 때 $0$ 을 도출하는 식과 함께 나타내어야 한다.


일단 삼항연산자를 사용하여 색인을 나타내보자.

0 if 180!=sum(a)else len(set(a))

또는

len(set(a)) if 180==sum(a)else 0

와 같이 나타낼 수 있다.

4.3.4. 논리곱 and

전술하였듯이 삼항연산자는 논리곱 and 을 사용함으로 더 간략화될 수 있다.

int(180==sum(a))and len(set(a))

이는 다음의 개념을 사용하여 1 B 를 더 줄일 수 있게된다.

Boolean 의 int 화

  • True -> 1: int(True), --(True)
  • False -> 0: int(False), --(False)

--(180==sum(a))and len(set(a))

위의 구현을 풀어 설명하면 다음과 같다.

  1. 180==sum(a)이면 $0$ 을 반환
  2. 180==sum(a)거짓이면 len(set(a)) 을 반환

이는 우리가 정확히 원하는 결과이다.

4.3.5. Boolean 색인

Boolean 색인 !


이는 상상하지도 못한 Python 문법이었다. 문법이라기보다 Python 특유의 처리방식에 따른 꼼수에 더 가깝겠지만..


Boolean 색인은 즉, 다음과 같은 코드가 오류없이 동작할 수 있음을 시사한다.

a = [1, 2, 3, 4]
print(a[True])
print(a[False])

실행결과

2
1

이상하게도 Python 에서는 리스트의 색인이 True 혹은 False 값을 갖더라도 오류 없이 알아서 1, 0 으로 해석하여 $1$ 번째, $0$ 번째 원소를 도출한다.


이는 무려 4 B 나 줄일 수 있는 핵심으로 작용한다.

180==sum(a)and len(set(a))

5. 제출 코드

1차

맞았습니다

a=[int(input())for _ in'   '];print(['Equilateral','Isosceles','Scalene','Error'][-(180!=sum(a))or len(set(a))-1])

논리합 (or) 를 사용하여 구현한 풀이이다.

['Equilateral','Isosceles','Scalene','Error'] 의 배열도 다르고 len(set(a))-1 이 덧붙여진 형태를 볼 수 있다.


원래 위 풀이로 포스팅을 작성 중이었으나 작성하다가 논리합 (or) 이 아닌 논리곱 (and) 을 사용하면 더 짧은 코드로 나타낼 수 있다는 사실을 발견하고 포스팅을 갈아 엎고 재작성하였다... ㅠㅠ


논리곱 (and) 은 논리합 (or) 에 비해 1 B 손해이기 때문에 다른 부분에서 더 많은 Byte 이득을 취해야 한다.

위에서 말한 Boolean 색인을 사용할 수 있다는 사실을 모른 채로 논리곱을 사용하여 구현하면 다음과 같다.

a=[int(input())for _ in'   '];print(['Error','Equilateral','Isosceles','Scalene'][--(180==sum(a))and len(set(a))])

위 구현으로는 114 B1차 제출 코드와 같은 Byte 를 갖는 결과가 도출되지만 Boolean 색인 을 사용한다면 이야기가 역전된다.

2차

맞았습니다

a=[int(input())for _ in'   '];print(['Error','Equilateral','Isosceles','Scalene'][180==sum(a)and len(set(a))])

6. 여담

6.1. 실수 유의

sum(a)==180 and len(set(a))

위와 같은 실수를 하지 않도록 유의한다! 한 Byte 한 Byte 가 아깝다!!!

6.2. 논리합과 Boolean 색인 동시 적용?

논리합 or 을 사용하면서 Boolean 색인 을 적용할 수는 없을까?


색인은 다음을 만족해야 한다.

  1. 180!=sum(a)이면 $0$ 을 반환
  2. 180!=sum(a)거짓이면 len(set(a)) 을 반환

180!=sum(a)or len(set(a))

A or B 에서 A 조건식은 일 때 자기자신을 반환한다.

하지만 A 가 그자체로써 이면서 $0$ 일 수 없기 때문에 모순된다.


그렇다면 A 조건식이 거짓 값을 가짐으로써 자기자신인 0 을 반환하도록 할 수 있을까?

  1. 180==sum(a)거짓이면 $0$ 을 반환
  2. 180==sum(a)이면 len(set(a)) 을 반환

--(180==sum(a))or len(set(a))

이것이 가능하다면 이 역시 Boolean 색인을 사용하여 다음과 같이 나타낼 수 있을 것이다.

180==sum(a)or len(set(a))

하지만 위 경우에도 180==sum(a)거짓 일 경우 자기자신이 아닌 len(set(a)) 을 반환하므로 역시 원하는 결과를 도출하지 않는다.

6.3. 삼항연산자와 논리연산자

정리해보면 삼항연산자를 논리연산자로 표현하기 위해서는 다음을 만족해야 함을 알 수 있다.

A if not A else B

A and B

  • A거짓 일 때, 자기자신을 반환한다.
  • A거짓 값을 갖는다. (ex. False, 0)

A if A else B

A or B

  • A 일 때, 자기자신을 반환한다.
  • A 값을 갖는다. (ex. True, 1, 2, -1)


관련 포스팅

728x90