1주차 : 개론, 시간복잡도
- 문제 해결 절차를 체계적으로 기술한 것
- 문제의 요구조건
- 입력과 출력으로 명시할 수 있음
- 알고리즘은 입력으로부터 출력을 만드는 과정을 기술
알-콰리즈미(al-Khwarizmi) : 페르시아 과학자, 근대 수학의 아버지
- 알고리즘 기술을 위한 언어
- 프로그래밍 언어보다 융통성이 있음
- 모호함이 없이 명령어를 정의하면 됨
(의사코드는 실제 프로그래밍 언어처럼 엄격한 문법을 따를 필요X)
1) 정확성(Correctness)
2) 수행시간
3) 사용하는 메모리 공간
4) 최적성(Optimality) : 해당 문제를 푸는데 있어 "가장 적은 수의 기본 연산"으로 수행되는 알고리즘
Algorithm Efficiency 평가 기준 : "수행시간"과 "사용하는 메모리 공간"
문제의 해를 구하는 여러 알고리즘이 있을 때, 어떤 알고리즘이 좋은가?
문제
입력 : 배열 a에 저장된 정수들 ( a = [25, 90, 53, 11, 23] )
출력 : 최솟값
1) 알고리즘 A (A, B, C 중 가장 효율적)
Algorithm Max(a, n)
m = a [0]
for i = 1 to n-1 // n은 입력크기
if m > a[i] then m <- a[i]
output m
2) 알고리즘 B
3) 알고리즘 C
Algorithm Max(a, n)
i = -1
flag = true
while(flag):
i += 1
m = a[i]
flag = false
for j = 0 to n-1:
if m > a[j] then flag = true
output m
알고리즘을 프로그래밍 언어로 구현하여, 직접 컴퓨터에서 실행하는 수행시간 측정
# Python기준
import time
start = time.time()
...
fininsh = time.time()
print(finish - start) // 단위 : second
- 수행되는 연산(step, operation, primitive operation)의 수를 계산
- 장점 : 코딩 환경에 독립적이고 구현을 하지 않은 상태에서 분석
- 수행 시간의 단위는?
- 기본연산(비교, 사칙 연산등) : 알고리즘 수행시간에 가장 큰 영향을 미치는 연산
<python 예시>
# 1. 리스트에서 item과 같은 리스트 원소의 위치를 반환. Item과 같은 원소가 없으면 -1을 반환
def seqSearch(list, item):
for i in range(len(list)):
if(list[i] == item):
return i
return -1
# 2. 리스트(배열)에서 최대값 찾는 문제
Algorithm arrayMax(list, n):
m = list[0]
for i in range(1, len(list)):
if m < list[i]:
m = list[i]
return m
기본 연산 : item과 리스트 원소의 비교
[최악의 경우 수행시간] W(n) = n
기본연산 : 두 원소의 비교
[최악의 경우 수행시간] W(n) = n-1
-> 위의 두 경우에는 입력 형태가 역순이 되더라도 수행시간이 같음
입력 크기가 작을 때는 알고리즘 효율성이 중요하지 않지만, 입력 크기가 충분히 크다면 알고리즘의 효율성이 중요하다.
즉, 증가율에 의한 분석을 점근적 분석(Asymptotic Analysis)라고 한다.
<함수의 증가율 비교 그래프 1>최고차 항만 남기고 나머진 모두 무시.
최고차 항에 곱해진 상수도 무시.
가능한 tight하게 나타냄 (Tight하지 않은 만큼 정보의 손실이 일어남)
점근적인 분석에서 실행되는 기본 연산 수에 의한 분석과
실행되는 문장 수에 의한 분석은 동일하다.
# 예제 1
S = 0
for i in range(1, n+1):
for j in range(1, n+1):
S = S + 1 # 기본 연산
# 예제 2
i = 1
while (i <= n):
S = S + 1 # 기본 연산
i = i * b
예제1
예제2
# 예제 3 : n개의 키들로 구성된 리스트 list에서 어떤 주어진 키(x)가 있는 위치를 찾아라.
# 3-1) 순차적 탐색(Sequential Search)
for index in range(len(list)):
if x == list[index]:
break
# 3-2) 이진탐색(Binary Search)
def binarySearch(list, key):
left = 0
right = len(list) - 1
while left <= right:
mid = (left + right)//2
if key == list[mid]:
return True # return mid
elif key < list[mid]:
right = mid - 1
else:
left = mid + 1
return False
예제 3-1
수행시간 분석
T(n) = 입력 크기 n인 정렬된 리스트에서 이진 탐색을 하는데 수행되는 키 비교 횟수
T(n)은 1번의 비교 후에 리스트의 1/2 즉, 앞 부분이나 뒷 부분을 재귀 호출하므로 아래와 같이 된다.
T(n) ≤ T(n/2) + 1
T(1) = 1
T(n) ≤ T(n/2) + 1
≤ [T(n/2)/2 + 1] + 1 = [T(n/2^2)] + 2
≤ [T(n/2^2)/2 + 1] + 2 = [T(n/2^3)] + 3
≤ ... ≤ T(n/2^k) + k = T(1) + k
if n = 2^k, k = log n
= 1 + log n = O(log n)
(소수 : 1과 자기 자신 이외의 약수를 가지지 않는 1보다 큰 자연수)
# 예제 4-1)
prime = True
for i in range(2,n):
if n%i == 0:
prime = False
break
if prime:
print('Prime')
else:
print("Not Prime")
# 예제 4-2)
prime = True
i = 2 # 2부터 연산을 시작하므로 (√n - 1)번 연산 -> O(√n)
while i * i <= n: # i라는 약수를 가지고 있는지 보는 것. (i가 √n이 될 때까지 반복)
if n % i == 0:
prime = False
break
i += 1
if prime:
print('Prime')
else:
print("Not Prime")
예제 4-1
예제 4-2
예제 4-2 알고리즘에 사용된 성질 증명
n이 소수가 아니면 2이상 √n 이하의 약수를 가진다.
증명 : n은 소수가 아니므로 두 자연수 p,q(2≤p≤q)의 곱으로 나타낼 수 있다.
n = pq
p는 √n보다 작거나 같다. 그렇지 않다면,
n = pq > √n * √n = n이 되어 모순이다.
# 예제 5-1)
f = open("input.txt", "r")
data = f.read()
data = data.split()
maxSum = int(data[1])
n = len(data)
for i in range(1,n):
for j in range(i,n):
sum = 0
for k in range(i, j+1):
sum += int(data[k])
if (maxSum < sum):
maxSum = sum
print(maxSum)
# 예제 5-2) pseudo code
maxSum = "-♾"
for i = 0 to n-1
sum = 0
for j = i to n-1
sum = sum + data[j]
if (maxSum < sum) then
maxSum = sum
# 예제 5-3)
maxContigSum = []
# data[0] ~ data[i]까지의 원소들에 대하여, data[i]에서 끝나는 연속하는 원소들의 합 중에서 최댓값
maxContigSum[0] = data[0]
for i in range(1, n):
if (maxContigSum[i-1] > 0):
maxContigSum[i] = maxContigSum[i-1] + data[i]
else: // 0보다 작으면 청산. (새로 시작하는게 맞다.)
maxContigSum[i] = data[i]
maxSum = maxContigSum[0]
for i in range(1, n):
if (maxSum < maxContigSum[i]):
maxSum = maxContigSum
예제 5-1
예제 5-2
예제 5-3
# 수행시간 분석 예제
# 1. O(1), θ(1)
S = n(n+1)/2
# 2. O(n), θ(n)
S = 0
for i in range(1, n+1):
S = S + 1
# 3. O(n^2), θ(n^2)
S = 0
for i in range(1, n+1):
for j in range(1, n+1):
S = S + j
# 4. O(log n), θ(log n) ... 이진수의 표현과 관련이 있음(이진수 자릿수를 역순으로 출력.)
while n != 0:
print(n%2)
n = n / 2
# 5. 두 수의 합이 같을 때. O(n), θ(n)
i = 0
j = n-1
while (i < j):
if (a[i] + a[j]) == S:
break
elif(a[i]+a[j] < S):
i += 1
else:
j += 1
# 6. O(n√n)
def prime_num_algorithm(n):
i = 2
while(i*i <= n):
if(n % i == 0):
return False
i += 1
return True
count = 0
for i in range(2, n-1):
if(prime_num_algorithm(n)): # Boolean
count += 1