어 그냥 이 파트의 컨셉 자체를 모르겠어서 되게 당황스러움
시간복잡도라는 개념 자체를 알아야 하는 건가?
내 생각에는 시간복잡도 개념을 알고 => 문제에서 제시된 알고리즘이 O(1)인 알고리즘을 알아채느냐가 중요한 것 같음
input() #입력 받음
print(1) #무조건 한 번
print(0) #상수 알고리즘이라 무조건 0
출력 조건
1. 첫째 줄에 코드1 의 수행 횟수를 출력한다.
=> 여기에선 단순하게 알고리즘을 읽고 수행 횟수가 무조건 1임만 알 수 있으면 되는 것 같음
나의 생각(?)
코드 자체에서 무엇을 얻어갈 수 있는 문제는 아닌 듯
1. 시간 복잡도 개념 정리
- 입력 크기 N과 관계없이 항상 같은 횟수 연산 => O(1)
- 다항식 f(N) = 1 * N^0 => 최고 차항 차수 0
- 알고리즘을 보고 시간 복잡도를 평가해 보는 경험
- 코드의 구조(반복문, 재귀, 호출 등)을 보고
- 얼마나 많은 연산이 실행되는지
- 입력 크기에 어떻게 비례하는지
파악하는 훈련
시간 복잡도
Big-O 표기법
Big-O 표기법 종류
=> 대충 그래프 떠올리면 다 이해됨
1.O(1)
O(1)는 일정한 복잡도(constant complexity)라고 하며, 입력값이 증가해도 시간이 늘어나지 않음
입력값의 크기와 관계없이 즉시 출력값을 얻어낼 수 있음
2.O(n)
O(n)는 선형 복잡도(linear complexity)라고 하며, 입력값이 증가함에 따라 시간 또한 같은 비율로 증가
3.O(logn)
O(logn)는 로그 복잡도(logarithmic complexity)라고 하며, Big-O 표기법 중 O(1) 다음으로 빠른 시간 복잡도
- BST(Binary Search Tree)가 여기에 해당
4.O(n^2)
O(n^2)는 2차 복잡도(quadratic complexity)라고 하며, 입력값이 증가함에 따라 시간이 n의 제곱수 비율로 증가하는 것을 의미
5.O(2^n)
O(2^n)은 기하급수적 복잡도(constant complexity)라고 하며, Big-O 표기법 중 가장 느린 시간 복잡도
- 재귀로 구현하는 피보나치 수열이 대표적인 O(2^n) 시간 복잡도 알고리즘
def fibonacci(n): if n <= 1: return 1 return fibonacci(n-1) + fibonnacci(n-2)만약에 내가 피보나치가 저 시간 복잡도인 걸 모른다고 하면 어떻게 추측할까?
- 재귀 형식의 알고리즘을 만났다
- 그 함수가 자기 자신을 어떻게 호출하는지 보고
- 그것을 기반으로 재귀식을 세우는 것
재귀식 세우는 원칙
1. 호출 횟수: 함수 F(n) 안에서 몇 번 호출하는가?
2. 서브 문제 크기: 호출할 떄 넘겨 주는 인자의 크기는 어떻게 변하는가?
3. 추가 오버헤드: 그 호출 외에 내부에서 하는 반복문/연산량은?
=> **T(n) = (호출 횟수) X T(서브 크기) + O(오버헤드)
예시
| 알고리즘 | 재귀 호출 구조 | 재귀식 | 해(시간 복잡도) |
|---|---|---|---|
팩토리얼 (fact(n)) | 1번 호출, 크기 n-1 | T(n) = T(n-1) + O(1) | O(n) |
이분 탐색 (binary_search) | 1번 호출, 크기 n/2 | T(n) = T(n/2) + O(1) | O(log n) |
| 퀵정렬 (평균) | 2번 호출, 크기 약 n/2 | T(n) = 2 T(n/2) + O(n) | O(n log n) |
피보나치 재귀 (fib(n)) | 2번 호출, 크기 n-1와 n-2 | T(n) = T(n-1) + T(n-2) + O(1) | O(φ^n) |
n = int(input())
print(n)
print(1)
n = int(input())
print(n*n)
print(2)
n = int(input())
print(n*(n-1)//2)
print(2)
n = int(input())
print(n**3)
print(3)
파이썬에서 제곱근 표시
n**n = n * n * n
=> 조합이라고 설명하는데 조합으로 넘어가는 부분이 이해가 안 됨
a1, a0 = map(int, input().split())
c = int(input())
n0 = int(input())
def f(n) :
return a1*n + a0
def g(n) :
return c*n
flag = 1
for i in range(n0, 100):
if f(i) > g(i):
flag = 0
break
print(flag)
-a₁, a₀, c, n₀ 값을 입력받는다.
-f(n) = a₁·n + a₀로 정의
-g(n)은 문제에서 O(n)이라고 했으니 g(n) = c·n이라고 설정
-f(n) ≤ g(n)을 만족하는지 검사
-모든 n ≥ n₀에 대해 만족해야 하므로, 특정 범위 (n0 ~ n0+100)까지 검사하고 조건을 만족하면 1, 아니면 0 출력
1. def로 f(n), g(n) 정의
- 처음에 그냥 f(n) = a1 * n + a0이라고 작성
2. g(n) = c * n으로 정의한 이유
- 문제에서 O(n)에 부합하는지를 묻고 있으므로
- 만약 문제에서 O(n^2)를 물었으면 g(n) = c*n^2로 정의했을 것
- 빅오 표기법에서 상수항은 무시할 수 있어서 그냥 c*n 해도 ㄱㅊ
3. 모든 n >= n0에 대해 성립하는지
- 모든 n에 대한 검사는 불가능
=> ** 귀납적으로 n0부터 약 100개의 값만 테스트 **
4. flag 변수 사용
- flag = 1를 놓고, 틀리면 flag = 0으로 바꿈
- 조건을 만족하는지 검사하고 빠르게 탈출할 때 자주 쓰이는 패턴임