(백준) 17266.어두운 굴다리 / with.파이썬에서의 반올림
문제 링크: https://www.acmicpc.net/problem/17266
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys
import math
n = int(sys.stdin.readline().rstrip())
m = int(sys.stdin.readline().rstrip())
x = list(map(int, sys.stdin.readline().rstrip().split()))
if m == 1:
# 가로등이 하나인 경우
print(n)
else:
height = []
# 시작 지점(0)부터 첫 번째 가로등까지
height.append(x[0] - 0)
# 마지막 가로등부터 끝 지점(n)까지
height.append(n - x[-1])
# 각 가로등 사이의 거리
for i in range(1, len(x)):
# 두 가로등 사이의 거리의 절반(올림)
dist = x[i] - x[i-1]
# ceil 함수는 소수점에 어떤 값이 오든 무조건 올림을 한다.
height.append(math.ceil(dist / 2))
print(max(height))
풀이
파이썬에서의 반올림
파이썬에서 //
연산은 정수 나눗셈의 몫 값에 소수점이 있다면 이를 버린다.
( 참고로 //
연산을 활용한 나눗셈에서 피연산자에 실수형이 있을 경우엔 반환되는 값의 자료형 또한 float형이다. )
즉, 5 // 2
를 하면 2가 출력된다. 왜냐고? 5 / 2
를 한 값은 2.5이니, 이 값에서 소수점(0.5)을 버리고 정수만 취하면 2니까 2를 반환하는 것이다. 자연스럽게 2.5가 나왔으니 이를 반올림해서 3을 return 하는 것이 아니라.
-> 아 그럼 -5 // 2
하면 -2.5니까 소수점 없애고, -2를 반환하겠네욤?
==> 아니다.
-> 엥 이게 무슨 말이에요… 아까 앞에서 정수 나눗셈일 때 //
연산은 소수점을 버리고 정수만 return 한다면서요.
==> 실은 //
연산은 floor division(내림 나눗셈)이다. 내림 나눗셈은 그냥 소수점을 버리는 것(이를 0을 절삭한다고 표현한다.)과 다르게 피연산자(피연산자란 a // b
를 했을 때 a나 b를 말하는거임.) 중 하나가 음수라면 결과는 음의 무한대 방향으로 반올림이 된다.
아니.. 왜 이렇게 만들었을까? 반로섬(파이썬 창시자)이 말하길, 나머지 연산에서 일관된 방향으로 계산하기 위해서 이렇게 했다고 한다.
나머지 연산은 b * q + r = a
를 말한다. 여기서 q는 몫이고 r은 나머지를 의미한다. 즉, 피연산자 a의 값은 ((b * 몫) + 나머지) 한 값과 같단거다.
진짜 그럴까? 일단 피연산자가 양수일 때의 예시로 5 / 2
를 대입해서 풀어보자.
- 2 * 2 + 1 = 5
오 그렇네.
그러면 반대로 피연산자에 음수가 있을 때, 즉 -5 / 2
를 풀어보자. 일단 몫에서 그냥 소수점을 버린값 (여기선 -2)으로 나머지 연산을 하면 어떻게 될까?
- 2 * -2 + 1 = -3
헐, a는 -5여야 되는데 -3이 나오네.
그러면 반로섬이 말한 것 처럼 내림 연산을 적용하면 나머지 연산에서도 값이 일관되게 나오나?
- 2 * -3 + 1 = -5
그렇다. 파이썬에서는 피연산자가 음수든 양수든간에 나머지 연산에서 일관된 값을 내기 위해 //
연산자는 내림 연산을 수행하는거다.
더 자세한 내용은 아래 링크에서..
반로섬이 말하는 파이썬에서 몫 연산이 내림 나눗셈인 이유
이제야 파이썬의 //
연산에 대해 쪼금 이해한 것 같다.
그럼 저 문제에선 distance / 2
만 하니까 distance가 홀수일 때는 나눈 값에 round(반올림) 함수를 씌우면 끝나겠네?
왜냐면 distance가 5일 때 가로등의 높이가 3이어야지만 모든 거리를 밝힐 수 있잖아.
근데 3이 나오기 위해선 나눈 값(2.5)에 반올림을 해야되니까.
==> 그것도 안된다.
왜? 파이썬에서 round 함수는 Banker’s Rounding 방식이다.
통상적으로 반올림이라는 것이 소수 첫째 자리가 5 이상일 때는 5의 앞자리에 +1을 해서 올리고, 소수 첫째 자리가 5 미만이면 그 값을 그대로 출력하는것 아닌가? (==사사오입)
하지만 파이썬에서 round 함수는
- 소수 첫째 자리가 5일 경우, 그 앞자리가 홀수인지 짝수인지에 따라 반올림을 결정한다.
- 소수 첫째 자리가 5인데 앞자리가 홀수라면 올리고, 짝수라면 내린다는 소리다.
- 즉 round(2.5)는 2로 내리고, round(3.5)는 4로 올린다. Banker’s Rounding
홀수 / 2
를 했을 때 소수점 5의 앞자리에 항상 홀수만 오는가?
당연히 아니다. (e.g. 5/2=2.5, 7/2=3.5, 9/2=4.5 …)
따라서 여기서 round() 함수를 사용하면 distance가 5나 9일 때는 우리가 원하는대로 반올림이 되지 않기에 해당 가로등 거리를 모두 밝히지 못할 것이다.
고로 저 문제에서는
- distance가 홀, 짝일때를 구분해서 몫에 +1을 하거나,
1 2 3 4
if dist % 2 == 0: height.append(distance // 2) else: height.append((distance // 2) + 1)
- math.ceil() 함수를 사용하는 것이 보다 맞다.
1
height.append(math.ceil(distance / 2))
math.ceil()은 소수 자리에 어떤 값이 오든간에 올림을 하는 함수이다.
이 문제에서 ceil() 함수를 써도 상관이 없는 이유는, 앞에서도 말했지만 distance에 2를 나누는 연산만 하기 때문이다.
즉, distance가 짝수일때는 애초에 소수점이 나오지 않기 때문이다.
파이썬에서 사사오입 반올림 함수 만들기
1
2
3
4
5
def round(num):
if num - int(num) >= 0.5:
return int(num) + 1
else:
return int(num)