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
이 연산의 핵심은 무조건적 True
나 False
의 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))
이는 다음의 개념을 사용하여 2 B
를 더 줄일 수 있게된다.
Boolean 의 int 화
True -> 1
:int(True)
,+True
False -> 0
:int(False)
,+False
+(180==sum(a))and len(set(a))
위의 구현을 풀어 설명하면 다음과 같다.
180==sum(a)
이 참이면 $0$ 을 반환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$ 번째 원소를 도출한다.
이는 무려 3 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 B
의 1차 제출 코드와 같은 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 색인 을 적용할 수는 없을까?
색인은 다음을 만족해야 한다.
180!=sum(a)
이 참이면 $0$ 을 반환180!=sum(a)
이 거짓이면len(set(a))
을 반환
180!=sum(a)or len(set(a))
A or B
에서 A
조건식은 참일 때 자기자신을 반환한다.
하지만 A
가 그자체로써 참 이면서 $0$ 일 수 없기 때문에 모순된다.
그렇다면 A
조건식이 거짓 값을 가짐으로써 자기자신인 0
을 반환하도록 할 수 있을까?
180==sum(a)
이 거짓이면 $0$ 을 반환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
)
관련 포스팅
'Computer Science > Baekjoon' 카테고리의 다른 글
[Baekjoon] 15723번 n단 논법 S1 (0) | 2024.08.23 |
---|---|
[Baekjoon] 1022번 소용돌이 예쁘게 출력하기 G3 <Short> (0) | 2024.08.19 |
[Baekjoon] 1068번 트리 G5 (0) | 2024.01.15 |
[Baekjoon] 1193번 분수찾기 S5 <Short> (0) | 2023.12.22 |
[Baekjoon] 2839번 설탕 배달 B1 <Short> (0) | 2023.11.12 |