* 파이썬 초정독 스터디 - 코딩테스트 합격자되기 파이썬 편 도서를 참고하여 작성된 글입니다.
고정소수점과 부동소수점의 차이
고정소수점 (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에 가까울 정도로 매우 작은 수이며, 실수 연산 능력의 오차를 나타내며, 아래 두 명제를 만족해야 한다.
- 어떤 실수를 가장 가까운 부동소수점 실수로 반올림 하였을 때 상대오차는 항상 Machine Epsilon 이하이다.
- 연산한 값과 비교할 값의 차이가 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 1add = 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 |