본문 바로가기
Algorithm

데이터 타입, 앱실론, 함수, 람다표현식

by isn`t 2024. 8. 24.

* 파이썬 초정독 스터디 - 코딩테스트 합격자되기 파이썬 편 도서를 참고하여 작성된 글입니다.

고정소수점과 부동소수점의 차이

  • 고정소수점 (Fixed-Point): 소수점의 위치가 숫자의 특정 자리에 고정되어 있는 형태로 우리가 일반적으로 표현하는 방식이다. 실수 123.45는 형식에서 소수점이 항상 소수점 아래 두 자리로 고정되어 있는 경우를 나타낸다.

  • 부동소수점 (Floating-Point): 숫자의 크기에 따라 소수점의 위치가 변할 수 있다는 의미인데, 예를 들어, 실수 123.45는 1.2345×10^2 로 표현하거나, 0.12345x10^3으로 표현할 수 있음. 같은 수를 의미하지만 소수점의 위치가 떠서 돌아나닐 수 있기 때문에 "부동소수점"이라고 함.

앱실론

  • 컴퓨터는 이진법을 이용하여 수를 표현하는데, 소수를 정밀하게 표현하는데 한계가 있다.
  • 따라서 실수를 표현할 때 컴퓨터는 부동소수점을 사용한다.
a = 0.1 + 0.1 + 0.1
b = 0.3
print(a+b) #0.6000000000000001
print(a-b) #5.551115123125783e-17

위 연산의 결과는 우리가 예상한 바와 다르다. 이처럼 부동소수점 연산에서는 정확한 값을 표현하는 데 한계가 있어, 아주 작은 차이를 허용하는 앱실론 개념이 필요하다.

import sys
print(sys.float_info.epsilon) #2.220446049250313e-16

앱실론은 0에 가까울 정도로 매우 작은 수이며, 실수 연산 능력의 오차를 나타내며, 아래 두 명제를 만족해야 한다.

  1. 어떤 실수를 가장 가까운 부동소수점 실수로 반올림 하였을 때 상대오차는 항상 Machine Epsilon 이하이다.
  2. 연산한 값과 비교할 값의 차이가 Machine Epsilon 보다 작거나 같다면 두 실수는 같은 값으로 본다

그래서 부동 소수점을 연산할 때는 '연산'이 아닌 '비교'의 개념으로 접근해야 한다.

import sys
a = 0.1 + 0.1 + 0.1
b = 0.3
result = a+b #0.6000000000000001
if math.isclose(a, b, abs_tol=sys.float_info.epsilon):
    print("same")
else:
    print("different")

컬렉션 데이터 타입

  • 여러 값을 담는 데이터 타입을 가리킴
  • 대표적으로 리스트, 튜플, 딕셔너리, 셋
  • 이 컬렉션 데이터 타입은 변경 가능한 객체와 변경 불간으한 객체로 나누며, 각각 뮤터블 객체, 이뮤터블 객체로 부름.

뮤터블 객체

  • 객체 생성 후 수정이 가능.
  • 리스트, 딕셔너리, 셋
  • [1,2,3] 에서 [2,3,4]로 실제 엘리먼트의 수정이 가능함.

이뮤터블 객체

  • 정수, 부동소수점, 문자열, 튜플
  • 값을 수정할 수 없다고 해서 a=1 -> a=2로 할 수 없다는 의미가 아님. 1이 저장된 공간에 값이 2로 수정되는게 아닌, 가리키는 위치가 바뀌는 참조의 개념임.

리스트와 튜플

  • 리스트와 튜플은 인덱싱, 슬라이싱이 가능하다는 공통점이 있고 튜플은 이뮤터블 객체라 삽입/삭제 등 수정이 불가능함
  • 그럼 수정도 가능한 리스트가 무조건 좋은 것이 아닌가?라는 의문이 들었는데, 목적 자체가 다름
  • 리스트
    • 데이터를 자유롭게 수정 또는 재배열하는 로직, 동적으로 데이터를 수정해야 하는 경우에 적합함.
    • 리스트는 가변적이기 때문에, 크기를 동적으로 변경할 수 있도록 설계되어 있음. 이를 위해 리스트는 실제로 필요한 메모리보다 더 많은 공간을 미리 할당해 둡니다. 이 여분의 공간은 리스트에 요소를 추가할 때 리스트를 자주 재할당하는 비용을 줄이기 위함.
    • 튜플: 튜플은 이뮤터블이므로 한 번 생성되면 크기나 내용이 변경되지 않습니다. 따라서 추가적인 메모리 공간을 미리 할당할 필요가 없습니다. 이로 인해 튜플은 리스트보다 적은 메모리를 사용합니다.
  • 튜플
    • 값이 절대 변경되면 안되는 경우에 무결성 보장을 위해 사용함
    • 값이 고정되어 있으므로 해시가 가능해짐.
    • 동적으로 수정이 발생하지 않으므로 고정된 메모리 공간을 할당하고, 메모리 사용을 최소화 할 수 있음
# 해시 가능한 객체의 해시값
print(hash("hello")) # 출력: 정수 (예: 123456789)
print(hash((1, 2, 3))) # 출력: 정수 (예: 456789123)

함수와 람다식

  • 여러 매개변수를 필요로 하는 복잡한 로직이 필요한 경우는 def로 함수를 정의해서 사용함
  • 한 줄 표현이 가능하고 일회성이 짙은 경우, 다름 함수의 인수로 사용될 경우는 람다식을 고려(ex: sorted, map, filter 함수와 함께 사용). 익명 함수를 만드는데도 사용.
    case 1
    add = lambda x, y: x + y
    print(add(5,3))

case 2

numbers = [1, 2, 3, 4]
squared_numbers = list(map(lambda x: x ** 2, numbers))
print(squared_numbers)  #[1, 4, 9, 16]

합성함수

  • 2개 이상의 함수를 활용하여 새로운 함수를 만드는 기법
  • 합성 함수는 람다식을 많이 활용함
def add_three(x):
    return x+3
def square(x):
    return x*x
composed_function = lambda x: sqaure(add_three(x))
print(composed_function(3))

이렇게 구현하면 아래에 비해서 훨씬 코드가 간결해진다.
예시에서는 단순한 연산이지만, 각 함수의 구성이 복잡해지고 여러 줄로 표현해야 하는 경우는 훨씬 간결하게 표현할 수 있음.

def add_three(x):
    return x+3
def square(x):
    return x*x
def composed_function(x):
    return square(add_three(x))

print(composed_function(3))

'Algorithm' 카테고리의 다른 글

재귀(recursive)  (3) 2024.09.15
스택(stack)  (2) 2024.09.07
배열  (8) 2024.09.01
시간 복잡도와 빅오 표기법  (0) 2024.08.17