Post

(백준) 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))

풀이

Image

파이썬에서의 반올림

파이썬에서 // 연산은 정수 나눗셈의 몫 값에 소수점이 있다면 이를 버린다.

( 참고로 // 연산을 활용한 나눗셈에서 피연산자에 실수형이 있을 경우엔 반환되는 값의 자료형 또한 float형이다. )

즉, 5 // 2를 하면 2가 출력된다. 왜냐고? 5 / 2를 한 값은 2.5이니, 이 값에서 소수점(0.5)을 버리고 정수만 취하면 2니까 2를 반환하는 것이다. 자연스럽게 2.5가 나왔으니 이를 반올림해서 3을 return 하는 것이 아니라.

-> 아 그럼 -5 // 2 하면 -2.5니까 소수점 없애고, -2를 반환하겠네욤?

alt text

==> 아니다.

-> 엥 이게 무슨 말이에요… 아까 앞에서 정수 나눗셈일 때 // 연산은 소수점을 버리고 정수만 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)에 반올림을 해야되니까.

alt text

==> 그것도 안된다.

왜? 파이썬에서 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)
This post is licensed under CC BY 4.0 by the author.