PS를 하든, 알고리즘을 구상하든 최솟값과 최댓값이 필요한 경우가 있다.

그럴 때 1e9와 같은 큰 수를 사용하기도 하지만, 경우에 따라 무한대가 필요하기도 하다.

Python3에는 inf를 표현하는 여러 가지 방법들이 있다.

 

pos_inf = float("inf")		# inf
neg_int = float("-inf")	# -inf

int_inf = int("inf")
# ValueError: invalid literal for int() with base 10: 'inf'

int_inf = int(float("inf"))
# OverflowError: cannot convert float infinity to integer

float 내장 함수 기능을 사용한다. 일반적으로 가장 많이 사용하는 방법이다.

음의 무한대를 저장할 때, float("-inf")가 아니라 -float("inf")로 저장해도 상관없다.

float 함수에서 제공하는 기능으로 int 함수로는 사용이 불가능하다.

int로 감쌀 경우, OverflowError가 뜰 수도 있고, 애초에 사용 불가능하여 ValueError가 뜰 수도 있다.

 

INF = 1e400		# inf

isINF_1 = 1e308 	# is not inf
isINF_2 = 1e309 	# is inf

충분히 큰 값 Var가 있다고 해보자.

놀랍게도 IEEE 754에 의해 1e{Var}는 inf으로 취급한다. 즉 float("inf")와 1e400은 같은 값로 취급한다는 이야기이다.

어디를 기준으로 inf로 인식할까 테스트 해보았다. Python3에서는 1e308은 1e+308로, 1e309는 inf를 출력한다.

 

import math

pos_inf = math.inf	# inf
neg_inf = -math.inf	# -inf

Mathematical Function 방법이다. 쉽게 말해 math 라이브러리를 사용한다.

해당 방법은 Python 3.5 이상 버전에서만 사용 가능하다.

 

from decimal import Decimal

pos_inf = Decimal("inf")	# Infinity
neg_inf = Decimal("-inf")	# -Infinity

float 자료형보다 정확하게 실수를 표현하기 위해 사용하는 Decimal(십진법) 모듈이다.

 └ https://docs.python.org/3/library/decimal.html

float와 동일하게 Decimal("-inf")로 적든, -Decimal("inf")로 적든 상관없다.

특이한 점은 inf가 아니라 Infinity를 return한다는 점이다.

 

import sys

int_pos_inf = sys.maxsize			# 922_3372_0368_5477_5807
int_neg_inf = -(sys.maxsize + 1)	# -922_3372_0368_5477_5808

Python3에서 float가 아니라 int 타입으로 무한대를 사용하고 싶을 때 사용한다.

무한대가 아니라 무지하게 큰 수이기는 하지만 말이다.

음의 무한대의 범위를 정확하게 할 필요가 없다면, 굳이 1을 더해주지 않아도 상관없다.

 

약 900경에 해당하는 수가 나오게 된 계기는 sys.maxsize이다. sys.maxsize는 해당 플랫폼의 포인터 사이즈를 return한다.

 └ 문자열이나 리스트 등의 구조 길이 제한을 위한 값이다.

 └ https://stackoverflow.com/questions/6918291/how-to-determine-word-size-in-python/6918334#6918334

32bit라면 2 ** 31 - 1, 64bit라면 2 ** 63 - 1이다. 계산기를 두드리면 동일함을 알 수 있다.

 

import math

math.isinf(something)	# True / False

마지막으로 어떤 값이 무한인지 아닌지 확인하기 위해서 math 라이브러리를 사용한다.

math.isinf() 함수를 사용하여, bool 타입으로 return해준다. Python 2.6 버전부터 사용 가능한 기능이다.

 

 

고민을 시작한 문제 출처 : https://shoark7.github.io/programming/algorithm/introduction-to-tsp-and-solve-with-exhasutive-search

참고한 문서 자료 : https://docs.python.org/3/library/decimal.html

참고한 문서 자료 : https://peps.python.org/pep-0754/

참고한 웹 사이트 : https://stackoverflow.com/questions/6918291/how-to-determine-word-size-in-python/6918334#6918334

참고한 웹 사이트 : https://stackoverflow.com/questions/24587994/infinite-integer-in-python

참고한 웹 사이트 : https://brownbears.tistory.com/549

참고한 웹 사이트 : https://da-nyee.github.io/posts/python-infinity/

참고한 웹 사이트 : https://shydev.tistory.com/9

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python Overflow  (0) 2023.08.17
Python Logical operator(AND, OR) 설명  (0) 2023.08.15
Python loop-else  (0) 2023.07.09
Python Underscore(_)  (0) 2023.06.30
Python map  (0) 2023.06.29

우연히 알게 되었는데, 파이썬에서는 while, for, try 구문과 else 구문을 같이 사용할 수 있다.

이를 loop-else라고도 부른다.

 

for x in range(3):
    print (f'iteration no {(x+1)} in for loop')
else:
    print ("else block in loop")
print ("Out of loop")

# OUTPUT
# >>> iteration no 1 in for loop
# >>> iteration no 2 in for loop
# >>> iteration no 3 in for loop
# >>> else block in loop
# >>> Out of loop

반복문과 else를 같이 사용하면 else 부분을 마지막에 반드시 실행한다.

이때 break로 반복문을 빠져나오면 else 부분을 실행하지 않는다.

 

for fruit in fruits:
    if fruit == "banana":
        break
else:
    raise ValueError('No banana flavor found!')

이를 이용하면 위와 같은 에러 발생으로 처리할 수도 있다.

정상적으로 확인을 하면 우리는 for문의 if의 break를 거친다.

break를 거쳐서 반복문을 빠져나왔기 때문에 else는 실행하지 않는다.

하지만 for문 전체를 다 돌았음에도 불구하고 break를 거치지 않으면, else로 가고 결국 에러를 발생한다.

 

x = 0
while x < 3:
    x += 1
    print (f'iteration no {x} in while loop')
else:
    print ("else block in loop")
print ("Out of loop")

# OUTPUT
# >>> iteration no 1 in for loop
# >>> iteration no 2 in for loop
# >>> iteration no 3 in for loop
# >>> else block in loop
# >>> Out of loop

while문도 for문과 유사하게 사용한다.

 

try:
    ''' something code '''
except:
    ''' something error '''
else:
    ''' something run '''
finally:
    ''' anyway do this '''

try에서 else는 살짝 사용이 다르다.

except는 에러가 발생했을 때, else는 에러가 발생하지 않았을 때 들어가는 부분이다.

except 없이 try-else만 사용하는 것은 불가능하다.

 

try - except - else - finally인데 이를 정리하자면 아래와 같다.

try: 실행하려는 구문

except: 에러가 발생했다면 실행

else: 에러가 발생하지 않았다면 실행

finally: 반드시 실행

 

 

고민을 시작한 문제 출처 : https://www.acmicpc.net/problem/2839
참고한 문서 자료 : https://www.tutorialsteacher.com/python/python-else-loop

참고한 문서 자료 : https://www.tutorialspoint.com/How-to-use-else-statement-with-Loops-in-Python

참고한 웹 사이트 : https://blog.doosikbae.com/entry/Fluent-Python-Chapter-15-Context-manager%EC%99%80-else-%EB%B8%94%EB%A1%9D

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python Logical operator(AND, OR) 설명  (0) 2023.08.15
Python inf  (0) 2023.08.14
Python Underscore(_)  (0) 2023.06.30
Python map  (0) 2023.06.29
Python print  (0) 2023.06.28

Python에서 _는 다양한 경우에 사용할 수 있다.

일반적인 _는 스네이크 표기법을 이용한 명명에서만 사용한다.

코딩 테스트를 진행하는 사람에게는 'for _ in range(N)'이 익숙할 수 있고,

혹은 '__init__(self)'나, '__name__ == __main__'으로 익숙할 수 있다.

아래는 파이썬에서 underscore(_)를 사용하는 방법들에 대해서 정리했다.

 

# Case 1: Separating Digits
decimal = 2_315_268	# 2315268

hexa = 0x_54_FA		# 21754

binary = 0b_0011_1111	# 63

1. 숫자 값의 자릿수 구분을 위한 구분자로 사용한다.

Python 3.6에 추가한 문법이다. 숫자 어디에 사용해도 상관없는 가독성을 위한 문법이다.

 

# Case 2: Throw-away variable
name, _, age = ["John", 2000, 22]
print(name, age)

first, *_, last = (1, 2, 3, 4, 5, 6, 7)
print(first, last)

for _ in range(3):
    print("Hello, World!")

2. 값을 무시하는 기능으로 사용한다.

보통 변수 필요 없이 반복할 때 for문에서 자주 사용한다. 가운데 예시처럼 unpacking(*)로 다중 무시도 가능하다.

 

# Case 3: PEP8
_single_leading_underscore: 한 모듈 내부에서만 사용하는 private 클래스/함수/변수/메소드를 선언
single_trailing_underscore_: 파이썬 키워드와의 충돌을 피하기 위해 사용
__double_leading_underscores: 하나의 문법 요소, 클래스간 속성명 충돌 방지를 위해 사용
__double_leading_and_trailing_underscores__: 스페셜 변수나 메소드에 사용(예시. __init__)

3. 특별한 의미의 네이밍으로 사용한다.

Python Coding Convention인 PEP8에서는 4가지의 underscore 활용을 소개한다.

 

# Case 4: i18n / l10n 
from django.utils.translation import ugettext as _

from django.http import HttpResponse

def translate_view(request):
  translated = _('This string will be translated.')
  return HttpResponse(translated)

4. 국제화(i18n) / 지역화(l10n) 함수 Convention(권장 가이드라인)으로 사용한다.

i18n / l10n 함수를 _로 바인딩하는 C Convention에서 유래하였다.

i18n / l10n 라이브러리인 gettext 내장 라이브러리 API 문서와 i18n / l10n을 지원하는 Django의 공식 문서에서도 사용다.

 

# Case 5: Holds the last result in interactive interpreter session
>>> 4+6
10
>>> _
10
>>> _ * 3
30
>>> _ * 20
600

5. 파이썬 인터프리터에서, 가장 마지막에 실행한 결과값을 저장한다.

이는 표준 CPython 인터프리터에서 먼저 사용했으며, 다른 파이썬 인터프리터에서도 똑같이 사용할 수 있다.

 

 

고민을 시작한 문제 출처 : https://www.youtube.com/shorts/WVG2IM1N1VY
참고한 문서 자료 : https://dbader.org/blog/meaning-of-underscores-in-python

참고한 문서 자료 : https://peps.python.org/pep-0515/

참고한 웹 사이트 : https://mingrammer.com/underscore-in-python/

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python inf  (0) 2023.08.14
Python loop-else  (0) 2023.07.09
Python map  (0) 2023.06.29
Python print  (0) 2023.06.28
Python Round(반올림) 요약  (0) 2023.06.24
map(function, iterable, *iterables)

map 함수의 기본적인 문법(구조와 매개변수)이다.

function 매개변수는 반복 가능한 객체에 실행할 함수를 입력 받는다.

 └ function: The function which is going to execute for each iterable

iterable 매개변수는 함수를 적용할 반복 가능한 객체를 입력받는다.

 └ iterable: A sequence or collection of iterable objects which is to be mapped

*iterables 매개변수는 iterable 객체를 뒤에 추가적으로 더 넣을 수 있다는 의미이다.

이때 iterable 객체가 비어있거나 default라면 ValueError를 발생한다.

 

print(map(int, ["1", "2"]))
# <map object at 0x000001B863CDADD0>

예를 들어 ["1", "2"]와 같은 문자열 리스트를 int 함수를 이용하여 mapping 한다고 해보자.

map() 함수의 결과는 map 타입으로 결과를 return한다.

그렇기에 list나 dict, tuple 같은 다른 타입으로 변환하여 사용해야 한다.

 

map(int, input().split())
list(map(int, input().split()))

코딩 테스트를 준비하다보면 가장 많이 쓰는 입력 map 활용이다.

공백(스페이스바)을 기준으로 띄어진 숫자들을 입력받는다.

 

def cube(n):
    return n ** 3
 
num = [2, 4, 6, 8]
result = map(cube, num)
print(list(result))	# [8, 64, 216, 512]

map 함수에 사용하는 함수는 내장 함수 외에 def로 선언한 함수도 가능하다.

이를 위해 입력받은 수를 세제곱해서 return하는 cube 함수를 작성했다.

num으로 선언한 list를 cube 함수로 mapping한 후, list 타입으로 다시 변환하는 예제 코드이다.

 

def Multiply(a, b, c):
    return a * b * c
 
lst1=[2, 4, 6, 8, 10, 12, 14, 16]
lst2=[1, 3, 5, 7, 9, 11, 15]
lst3=[2, 3, 5, 7, 11, 13, 17]
 
result = list(map(Multiply, lst1, lst2, lst3))
print(result)	# [4, 36, 150, 392, 990, 1716, 3570]

위의 매개변수 *iterables에서 언급한 것처럼 여러 개를 mapping할 수도 있다.

Multiply 함수를 작성하고 lst1, lst2, lst3를 mapping하는 예제 코드이다.

 

list(map(lambda x: x * 2, [5, 4, 3, 2, 1]))
# [10, 8, 6, 4, 2]

map의 function에는 함수식이 들어간다. 이말은즉슨 lambda도 사용 가능하다는 것이다.

입력받은 x를 2배 해주는 lambda 식을 작성하여 mapping하는 예제 코드이다.

 

 

고민을 시작한 문제 출처 : https://school.programmers.co.kr/learn/courses/30/lessons/150370
참고한 문서 자료 : https://docs.python.org/3/library/functions.html#map

 

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python loop-else  (0) 2023.07.09
Python Underscore(_)  (0) 2023.06.30
Python print  (0) 2023.06.28
Python Round(반올림) 요약  (0) 2023.06.24
Python Round(반올림) 설명  (0) 2023.06.24
print(10)

Python3에서 위와 같은 코드를 실행하면 10을 출력한다.
 

print(print(10))

이런 경우에는 어떨까?
10을 출력하고 print(10)을 출력하라는데 문자열이 아니다. 무엇을 출력할까?
 

result = print(10)
print(result)

print(print(10))의 코드는 위처럼 변경할 수 있다.
Python3부터 print()함수는 None을 반환(return)한다.
따라서 print(result)의 출력 결과는 None이다.
 

def func():
    ''' do something '''
    print( ''' print something ''' )
    
result = func()
print(result)

함수를 작성하고 함수의 return을 result에 저장한 뒤, result를 출력하는 코드를 작성했다고 해보자.
함수를 이용하여 코드를 작성하는 대부분의 경우에 위와 같은 형식을 따를 것이다.
 
이때 func()의 return이 없으니까 뭐 아무 것도 출력하지 않겠지~라는 생각으로 실행한다면,
print()의 반환값 때문에 result는 None을 출력한다.
혹은 출력하는 방식이나 값이 list인지, dict인지, 문자열인지에 따라서도 달라진다.
 └ 예를 들면 [None, None, None]와 같은 형태로 출력할 수도 있다는 이야기이다.
고로 내게 필요한 코드만 있는지 확인하는 것이 중요하다.
 

print(...)
#     print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
#     Prints the values to a stream, or to sys.stdout by default.
#     Optional keyword arguments:
#     file:  a file-like object (stream); defaults to the current sys.stdout.
#     sep:   string inserted between values, default a space.
#     end:   string appended after the last value, default a newline.
#     flush: whether to forcibly flush the stream.

return 값 외에도 Python docs에서 print를 찾아보면 많은 parameter를 갖는 것을 볼 수 있다.
sep는 출력하는 값 사이에 끼워서 출력할 값을 설정한다. default는 공백(ASCII 32번 SP)이다.
end는 문장 맨 뒤에 출력할 값을 설정한다. default는 \n이다.
file은 file에 값을 쓸 지 설정한다. default는 sys.stdout으로 출력이다.
 └ 예를 들어 print("Hello, World!", file = f)라고 하면, 출력 대신 f라는 file에 작성한다. 물론 f을 선언해둔 상태여야 한다. 
flush는 buffered 상태일 때 값을 바로 출력한다. default는 False이다.
 └ 기본적으로 print()는 buffer에 값을 저장한 뒤, 그 값을 출력한다. 이런 buffering 과정을 생략한다는 것이다. 
 └ False 상태임에도 end parameter가 \n인 경우 내용을 바로 flush한다.
 
어려운 문제들에 도전하고, 간단한 문제들을 최대한 간결하게 풀려 노력하고 있다.
어째 그럴수록 기본 내장함수나 동작 구조들에 대해서 더 자세하게 알아야 하는 상황이 발생한다.
기본에 충실하자.
 
 
고민을 시작한 문제 출처 : https://www.acmicpc.net/problem/6603
참고한 문서 자료 : https://stackoverflow.com/questions/37084246/printing-using-list-comprehension
참고한 문서 자료 : https://docs.python.org/ko/3/library/functions.html#print
참고한 웹 사이트 : https://velog.io/@janeljs/python-print-sep-end-file-flush

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python Underscore(_)  (0) 2023.06.30
Python map  (0) 2023.06.29
Python Round(반올림) 요약  (0) 2023.06.24
Python Round(반올림) 설명  (0) 2023.06.24
Python lambda  (0) 2023.04.03

파이썬3의 반올림은 오사오입(Round half of even) 방식을 사용한다.

이를 사사오입으로 구현하기 위해서는 세 가지의 방법이 존재한다.

 

def roundTraditional(val, digits):
    return round(val + 10**(-len(str(val)) - 1), digits)

첫째, 함수를 작성하여 반올림을 계산한다.

 

Ex)
if num >= 0:    # num is positive
   print(math.floor(num + 0.5))
        
else:           # num is negative
   print(math.floor(num - 0.5))

둘째, 올림자리(0.001) * 5를 더해주고 버림 연산을 한다.

 

num1 = 0.0065		# Round half up
num2 = 0.0065000000001	# Round half of even

print(f'{num1 : .3f}')	# 0.06
print(f'{num2 : .3f}')	# 0.07

셋째, 올림자리 근처의 0.0000001과 같은 미세한값을 더해준다.

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python map  (0) 2023.06.29
Python print  (0) 2023.06.28
Python Round(반올림) 설명  (0) 2023.06.24
Python lambda  (0) 2023.04.03
Python f string format  (0) 2023.04.01

사건의 발달은 이러하다.

어느날처럼 1일 1백준을 하려고 문제를 보다가 '브론즈 1'인데 실패인 문제를 발견했다.

'평균은 넘겠지'라는 쉬운 문제였다. https://www.acmicpc.net/problem/4344

문제는 '사람 수와 점수가 주어졌을 때, 평균을 넘는 사람 수를 백분율로 표시하라'는 것이다.  

 

왜 실패했을까?라는 의문으로 제출을 살펴보았는데 약 2년 전에 내가 실패했었다.

2년 전의 나약한 내 자신을 보면서, 지금의 나는 얼마나 성장했을지 궁금했다.

어라? 전혀 성장하지 않았나? 코드에는 전혀 문제가 없어보이는데... 왜 틀렸지?

 

그래서 처음부터 새롭게 코드를 작성해보았고, 틀렸다.

왜 틀렸는지 4시간에 걸쳐 원인을 찾았고, 구조에 대한 이해를 했다.

내가 언어에 대한 이해가 부족한 것도 맞았지만, 문제 조건도 오류가 있다고 생각한다.

이를 위해서 우선 반올림의 개념에 대해서 정확하게 짚고 넘어가야 할 필요가 있다.

 

반올림이란,
근갓값을 구할 때 끝수를 처리하는 방법으로, 최소 단위로 나눈 나머지가 최소 단위의 절반에 미치지 못하는 경우 버리고 초과하는 경우 올리는 셈 방법이다.

 

다양한 반올림 방법

반올림에는 1가지 종류만 있는 것이 아니다.

그렇다면 이런 반올림은 왜 종류가 다양한 걸까? 너무 당연한 이유이다.

경우에 따라 '어디까지가 절반인지 애매하기 때문'이다.

숫자에는 0부터 9까지의 10개의 숫자가 있다. 이때 정확하게 중간은? 4와 5이다.

그럴 때 4와 5 중 무엇을 기준으로 버리고 올림을 할지에 따라서도 종류가 나뉜다.

또한 0이 아니라 1을 시작으로 하면 9개의 숫자가 있는데, 이때 정확하게 중간은 5가 된다.

이 5를 기준으로 버릴지, 올릴지, 아니면 다른 규칙에 의해 판단할지에 따라 종류가 나뉘기에 다양한 경우가 있는 것이다.

 

말이 어렵지 반올림이란 결국, 무엇을 기준으로 내림과 올림을 할지 정하는 방법을 말한다.

보통 초등학교 고학년 때 배우는 개념으로 당시 수업을 기억하는 사람은 몇 없겠지만...

우리가 흔히 사용하는 반올림 방법은 사사오입(Round Half Up)이다.

이는 0부터 4까지는 버리고, 5부터 9까지는 올리는 반올림 방법이다.

 

이와 비슷하게 많이 사용하는 방법은 오사오입(Round Half of Even)이다.

0부터 4까지는 버리고, 6부터 9까지는 올린다.

만일 5라면 앞자리가 짝수라면 버리고, 홀수라면 올림하는 조금은 복잡한 방법이다.

공학이나 자연과학, 경제학에서 사용하는 반올림은 오사오입 방법이다.

 

직접 해보면 이상하게 느껴지겠지만, 통계적으로는 합리적이기 때문이다.

└ 오사오입) 0.5를 반올림하면 0이지만, 1.5를 반올림하면 2가 된다.

맨 뒷자리가 필연적으로 손실되는 사사오입보다, 수마다 확률적으로 다른 방식을 취하는 오사오입이 차이를 줄여주기 때문이다.

이를 밑바탕으로 이해해야 프로그래밍의 반올림을 이해하기 편하다.

 

https://www.acmicpc.net/board/view/120014

나처럼 같은 문제에 대해서 맞왜틀?을 물어본 질문을 찾았고, 답변에서 정답을 얻었다.

위의 사진은 링크를 타고 들어가면, 답변에 달린 링크를 캡처한 사진이다.

반올림 방식이 모호하다? 사사오입 방식을 사용해야 한다?

 

사사오입(산술적 반올림) - Round half up 오사오입(뱅커스 라운딩) - Round half of even
Python2
C
C++
Object-C
Go
Ruby
Scala
PHP
Javascript
Java
Erlang
Fortran
Excel
Google Spread Sheet
MySQL
Hive
Redshift
BigQuery
Oracle
SQLite
Matlab
Tableau

ㆍ Linux shell (printf)
R
Python3
C#
Kotlin
Visual Basic
Julia
Pascal
Cobol
Mathematica
WolframAlpha

오사오입 방식은 금융권에서 자주 사용하는 방식으로 Banker's Rounding(뱅커스 라운딩)이라고 부른다.

또한 이 방법은 Gauss법을 기반으로 하는 방식으로 Gaussian Rounding(가우시안 라운딩)이라고도 부른다.

이 방법(오사오입)은 IEEE 754의 표준 반올림 규악이다.

 └ “bankers rounding,” and is the default IEEE 754

따라서 특정 몇 개의 언어와 새로 나오는 언어들은 오사오입이 default일 확률이 높다.

위의 표를 보면 Python3는 오사오입의 방식으로 반올림을 진행한다.

또한 대부분의 반올림이 필요한 코딩 문제는 IEEE 754에 따라 오사오입의 방식으로 채점한다.

하지만 4344번 문제는 사사오입의 방식으로 문제를 채점하기에 사소한 오차가 생겼고

그것이 결국 오답으로 이어졌다.

 

def roundTraditional(val, digits):
    return round(val + 10**(-len(str(val)) - 1), digits)

이제 나에게 필요한 것은 오사오입을 사용하는 Python3에서 사사오입을 구현하는 방법이다.

Python3에는 총 3가지의 방법이 있다고 한다.

첫 번째, 사사오입 구현을 위한 함수를 구현하는 것이다.

두 번째, 0.5를 더해주고 내린다.

세 번쨰, 0.00001 같은 아주 작은 수를 더하고 round 함수를 사용한다.

 

첫 번째 방식의, 함수를 구현하는 코드는 위와 같다. (stackOverflow에서 퍼왔다.)

 └ https://stackoverflow.com/questions/31818050/round-number-to-nearest-integer/38239574#38239574

반올림하려는 val와 몇 번째자리까지 표현할지에 대한 digits를 변수로 받아들인다.

이건 함수로 수학적으로 계산했다고 하니 이해할 수 있다.

 

두 번째 방식도, 오사오입의 수학적 원리를 알면 이해할 수 있었다.

 └ 앞이 짝수면 내리기 때문에, 먼저 0.5를 더해주고 내려줘야 한다.

 └ 이때 계산하는 수가 음수라면 더하는 것이 아니라 빼줘야 한다.

 └ 0.5를 더한다기보다는 정확하게는, 표시하려는 자릿수 아래 자리수에 .5를 더해준다.

 └ Ex) 0.005를 둘째자리까지 표현하려면 0.005를 더해준다.

 

num1 = 0.5
num2 = 0.5000001

print(round(num1, 0))	# 0.0
print(round(num2, 0))	# 1.0

하지만 세 번째 방식은 이해가 안 됐다.

0.00001 같은 작은 수를 더해주는 것은 왜...? 사사오입의 방식이 되는지 알 수 없었다.

이를 위해서는 부동소수점으로 소수를 저장하는 방법을 또 알아야 한다. 여기서는 간단하게만 정리한다.

 └ 부동소수점도 IEEE 754 규격을 따른다.

 └ 부동소수점에 대한 자세한 설명은 따로 정리해놓았다. https://miny-genie.tistory.com/171

 

num1 = 0.0055
num2 = 0.00551

print(f'{num1 : .3f}')	# 0.005
print(f'{num2 : .3f}')	# 0.006

0.0055를 소수 셋째짜리까지 반올림한다고 할 때, 오사오입 방식으로 계산하면 0.006이 나와야 한다.

하지만 위처럼 0.0055는 0.005가 나오고, 0.00551은 0.006이 나온다.

 

num1 = 0.0055
num2 = 0.00551

print(f'{num1 : .64f}')	# 0.0054999999999999996808108804202674946282058954238891601562500000
print(f'{num2 : .64f}')	# 0.0055100000000000001407207683712385914986953139305114746093750000

num1 = 0.0065
num2 = 0.00650001

print(f'{num1 : .3f}')	# 0.006
print(f'{num2 : .3f}')	# 0.007

0.0055를 저장했지만 부동소수점에 의해 실제 값은 0.005499999...562500000 값을 저장한다.

그래서 오사오입 연산을 하더라도 0.0054에서 연산을 수행하는 셈이다.

그럼 4는 버리기에 당연히 결과는 0.005가 나온다.

이러한 소수 저장 방식의 결손치를 채워주기 위해 0.00001 같은 작은 수를 더해준 뒤, 반올림하는 것이다.

 

이를 바탕으로 0.0065라는 수를 반올림해보자.

num1은 그냥 0.0065를, num2는 0.00000001이라는 아주 작은 수를 더했다.

둘 다 오사오입의 연산을 수행하지만 num2의 경우에는 사사오입으로 처리됨을 알 수 있다.

고로 num1은 0.006을, num2는 0.007을 출력한다.

 

num1 = 0.0065
num2 = 0.00650001

print(f'{num1 : .3f}')	# 0.006
print(f'{num2 : .3f}')	# 0.007

num1 = 0.0065
num2 = 0.006500000000000001

print(f'{num1 : .3f}')	# 0.006
print(f'{num2 : .3f}')	# 0.006

이때 주의할 점은 너무 작은 수를 더해주는 거는 또 안 된다는 것이다. 적당히 작은 수를 더해야 한다.

너무 작은 수를 더해버리면 결손치를 보완을 못 해주기 때문이다.

적당히 작은 수를 더해주자. 

 

결국 이 문제는 사사오입이라는 반올림으로 채점하는데,

오사오입을 사용하는 언어로 문제를 푼 것, 문제에서 자세하게 명시하지 않은 것 두 가지가 중첩하여 오답이 뜬 거다.

우여곡절 끝에 완전히 이해했다.

지식이 늘었고, 나는 성장했다.

지금 실력으로 브론즈 1에서 4시간이 걸릴 거라고는 상상도 못해 공부의 필요성을 더 체감했다.

 

P.S.1

for k in [*open(0)][1:]:N,*l=map(int,k.split());print(f"{sum(i*N>sum(l)for i in l)/N+9**-9:.3%}")

정답을 맞추고 다른 사람들은 어떻게 풀었나 궁금해서 조금 돌아다니다 엄청난 것을 발견했다.

comprehension으로도 이 문제를 풀 수 있는 거였어...?

나는 해석에 성공했다.

이 글을 읽는 누군가가 있다면 당신도 도전해보길 바란다.

 

P.S.2

위에서 정리 & 설명할 때 0.5와 00.5 같은 예시만 있는 이유가 있다.

반올림이라 당연하다. 0부터 4는 버리는 것이 확실하고 6부터 9는 올리는 것이 확실하다.

많은 반올림 방법이 생긴 이유이자 이 글의 핵심이다.

5를 올려야 할지 내려야 할지 애매모호하기 때문이다. 그렇기에 예시의 마지막 숫자는 늘 5이다.

글을 쓰다 마치 게슈탈트 붕괴처럼 헷갈려서 적는다.

 

P.S.3

채점 방식에서 절대/상대 오차를 허용하는 문제가 '스페셜 저지'임을 추가로 알게 되었다.

 

또한 알고리즘 용어들도 알게 되었다. 공부가 더 필요하다...

ㆍ Accepted (AC) 
 Presentation Error (PE)
 Wrong Answer (WA)
 Runtime Error (RE)
 Time Limit Exceeded (TLE)
 Memory Limit Exceeded (MLE)
 Output Limit Exceeded (OLE)
 Compilation Error (CE)
 Restricted Function (RF)
 Internal Error (IE)

 

P.S.4

이 문제를 도와주는데 흔쾌히 시간을 내 준, 내 친구에게 감사의 말을 보낸다.

고맙다 주인장놈아! https://mekain80.tistory.com/

 

메카인의 지식창고

 

mekain80.tistory.com

 

 

+ 2023.06.27 수정

아니 이게 왜 스페셜 저지가 추가...?

정확하게 알았으니 감사하고 넘어가도록 하자

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python print  (0) 2023.06.28
Python Round(반올림) 요약  (0) 2023.06.24
Python lambda  (0) 2023.04.03
Python f string format  (0) 2023.04.01
Python zip  (0) 2023.03.30
lambda 매개변수 : 표현식

lambda 표현식 함수 한 줄로 여러 가지 다양한 표현을 할 수 있다.

sort, string, map 같은 곳에 다양하게 사용할 수 있는데 lambda 사용이 미숙하여 공부의 필요성이 느껴졌다.

 

# Using def function
def sum(x, y):
    return x + y
    
print(sum(10,20))	# 30


# Using lambda
print((lambda x, y: x + y)(10, 20))	# 30

 

Python docs에 실려있는 예제 코드이다.

sum(x, y)로 두 변수를 더하는 함수를 작성했다고 한다.

이를 lambda의 매개변수에 x와 y를, 표현식에 x + y을 넣어준다.

그 후 (10, 20)의 값을 넣어주면 함수와 같은 결과가 나온다.

.

list(map(lambda x: x ** 2, range(5)))
# [0, 1, 4, 9, 16]

map의 조건식으로 lambda를 작성할 수도 있다. // map(function, iterable) 형태

range(5)를 돌면서 0, 1, 2, 3, 4를 x로 취급한다.

매개변수 x ** 2 연산을 list로 묶어 [0, 1, 4, 9, 16]이 된다.

 

words = {'candle':6, 'ant':3, 'equal':5}

sorted(words.items(), key = lambda k: k[0])	# [('ant', 3), ('candle', 6), ('equal', 5)]
sorted(words.items(), key = lambda k: -k[0])	# [('equal', 5), ('candle', 6), ('ant', 3)]
sorted(words.items(), key = lambda k: k[1])	# [('ant', 3), ('equal', 5), ('candle', 6)]

dictionary를 정렬하는 것도 가능하다.

items(), keys(), values()처럼 다양한 조건이 가능하지만 여기서는 items()로 진행하였다.

 

sorted의 키로 lambda 함수를 사용하였다.

가장 첫 번째는 words.items()의 첫 요소로 정렬하였다. (k[0])

candle, ant, equal 순이었던 dictionary가 ant, candle, equal 순으로 정렬된 것을 확인할 수 있다.

두 번째는 -k[0]를 키로 사용했는데, - 연산자를 붙이면 역순으로 정렬하는 것이다.

마지막 세 번째로 k[1] (dictionary의 두 번째 원소)로 정렬하여 숫자 오름차순으로 정렬한 것을 알 수 있다.

 

lst = [(1, 2), (0, 1), (5, 1), (5, 2), (3, 0)]

sorted(lst, key = lambda x : (x[0], -x[1]))
# [(0, 3), (0, 1), (1, 5), (1, 4), (1, 3), (2, 4)]

lambda는 다중 조건도 가능하다.

조건을 보면 x[0]으로 먼저 오름차순 정렬을 하고, x[1]로 내림차순 정렬을 하라고 되어있다.

lst의 원소들을 순서대로 x 값으로 받아들여 정렬을 한다.

정렬된 결과(주석)를 보면 첫 원소는 오름차순인 것을 확인할 수 있다.

그리고 첫 원소가 같을 때 두 번째 원소는 내림차순인 것을 확인할 수 있다.

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python Round(반올림) 요약  (0) 2023.06.24
Python Round(반올림) 설명  (0) 2023.06.24
Python f string format  (0) 2023.04.01
Python zip  (0) 2023.03.30
Python if comprehension  (0) 2023.03.28
name = input()		# steve
age = int(input())	# 23

string = name + "is" + str(age) + "years old."
print(string)		# steve is 23 years old.

보통 문자열을 더하는(이어주는) 코드를 짤 때 '+' 연산자를 사용한다.

이럴 때 문제점은 전부 string으로 형반환해줘야 한다.

그리고 각 문자열에 " "를 사용해 문자열임을 명시해줘야 한다는 점이다.

 

name = input()		# steve
age = int(input())	# 23

# Example 1
string = f"{name} is {age} years old."
print(string)		# steve is 23 years old.

# Example 2
string = f"He is {age-3} years old in 2020."
print(string)		# He is 20 years old in 2020.

Python 3.6 이후 버전부터는 이를 f-string으로 해소할 수 있다.

Example 1처럼 f" " 안에 문자열을 작성하고, { } 안에 매개변수를 적어주면 그에 대응하는 값을 출력한다.

Example 2처럼 { } 안에 계산식을 입력하는 것도 가능하다.

 

# string : s / int : d / float : f
# left : null or < / center : ^ / right : >

text = "Never say never"
print(f"{text:20s}")    # Never say never
print(f"{text:^20s}")   #   Never say never
print(f"{text:>20s}")   #      Never say never

number = 123456789
print(f"{number:<20d}") # 123456789
print(f"{number:^20d}") #      123456789
print(f"{number:>20d}") #            123456789

pi = 3.141592
print(f"{pi:<15f}")     # 3.141592
print(f"{pi:^15f}")     #    3.141592
print(f"{pi:>15f}")     #        3.141592

이때 서식 지정자를 사용하여 글자를 정렬하는 것도 가능하다.

{ } 안에 colon(:)을 기준으로 왼쪽에는 변수를, 오른쪽에는 '정렬기호 + 자릿수 + 서식 지정자' 알파벳으로 적어준다.

< ^ > 는 각각 왼쪽, 가운데, 오른쪽 정렬을 하라는 정렬 기호이다.

이때 왼쪽 정렬은 < 대신 아무것도 쓰지 않아도 된다. (text 예시)

그리고 변수형에 따라 s(문자열), d(정수), f(실수)를 맞춰 작성해 주어야 한다.

 

number = 3.141592
print(f"number is {number:.2f}")	# number is 3.14

number = 1
print(f"number is {number:.2f}")	# number is 1.00

소수점 자리를 지정해줄 수도 있다.

{ } 안에 colon(:)을 기준으로 왼쪽에는 숫자를, 오른쪽에는 ".자릿수f" 형태로 적어준다.

위의 코드처럼 number:.2f로 적게 되면 number 변수를 소수점 둘째 자리까지 출력하라는 의미이다.

정수여도 지정해준 자리까지 패딩하여 출력한다.

 

 

고민을 시작한 문제 출처 : https://www.youtube.com/shorts/X4E4aQriiwI

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python Round(반올림) 설명  (0) 2023.06.24
Python lambda  (0) 2023.04.03
Python zip  (0) 2023.03.30
Python if comprehension  (0) 2023.03.28
Python enumerate  (0) 2023.03.28

Python에는 다양한 편리한 내장 함수들이 있다.

절댓값을 계산해주는 abs, 형식을 지정해주는 format, 거듭제곱을 계산해주는 pow 등.

그 중 하나가 zip 함수이다.

 

num = [1, 2, 3]
char = ["A", "B", "C"]

zip_type = zip(num, char)
# <zip object at 0x000002710EF1B880>

zip_list = list(zip(num, char))
# [(1, 'A'), (2, 'B'), (3, 'C')]

zip은 동일 개수로 이루어진 다른 자료들을 묶어주는 함수이다. (쉽게 생각하면 mapping이다.)

이때 zip 객체로 반환하기 때문에 출력하기 위해서는 unpakcing이나 형변환이 필요하다.

 

compare1 = [1, 2, 3]
compare2 = ['a', 'b', 'c', 'd']

zip1 = zip(compare1, compare2)
# (1, 'a'), (2, 'b'), (3, 'c')

zip2 = zip(compare1, compare2, strict=True)
# ValueError: zip() argument 2 is longer than argument 1

zip은 동일 개수 객체를 함수라고 했는데 길이가 다르다면 어떻게 될까

기본적으로는 '가장 적은 객체를 기준'으로 나머지는 전부 잘린다. (zip1의 경우)

 

zip 함수는 zip(iterable, strict=false)가 기본 형태인데 strict를 True로 설정하면 길이가 다를 때 오류를 반환한다.

zip2의 경우처럼 ValueError가 뜨며 어떤 argument가 얼마나 길이가 차이가 나는지 확인이 가능하다.

 

import string

seq1 = string.ascii_lowercase
seq2 = "Abcdefghijklmnopqrstuvwxyz"

zip_seqs = zip(seq1, seq2)
#print(list(zip_seqs))

enum_seqs = enumerate(zip_seqs)
#print(list(enum_seqs))

for i, (a, b) in enum_seqs:
    if a != b:
        print(f'index: {i}')		# index: 0

zip을 응용하면 위와 같은 코드를 작성할 수 있다.

string 라이브러리를 import하여 string.ascii_lowercase를 사용할 수 있는데 이는 소문자 알파벳 a부터 z까지이다.

 

seq1에는 알파벳 소문자를, seq2에는 임의의 문자열을 입력한다.

이를 zip으로 같은 인덱스끼리 매핑한 튜플을 만들고, enumerate로 인덱스를 추가한다.

그 후 반복문에서 unpacking하여 seq1과 seq2의 문자가 다를 때의 index를 출력한다.

 

 

고민을 시작한 영상 출처 : https://www.youtube.com/shorts/rDfvSE4AuwM

참고한 문서 자료 : https://docs.python.org/ko/3/library/functions.html#zip

'Computer Science > 파이썬(Python)' 카테고리의 다른 글

Python lambda  (0) 2023.04.03
Python f string format  (0) 2023.04.01
Python if comprehension  (0) 2023.03.28
Python enumerate  (0) 2023.03.28
Python time module  (0) 2023.03.27

+ Recent posts