# 칸아카데미 알고리즘 강의
https://ko.khanacademy.org/computing/computer-science/algorithms
# 이진 검색
# 수도 코드
- min = 0 이고 max = n-1 입니다. 2.max < min, 이라면 멈춥니다. 타겟이 배열에 존재하지 않습니다. -1을 반환합니다.
- 'guess'를 'max'와 'min'의 평균으로 계산하고 (정수가 될 수 있도록) 내림합니다.
- 배열[guess]가 타겟과 같다면 멈춥니다. 찾았습니다! guess를 반환합니다.
- 만약 추측이 너무 낮았다면, 즉 배열[guess] < 타켓이라면, min = guess + 1로 바꿉니다.
- 그렇지 않다면 추측이 너무 높습니다. max = guess - 1로 바꿉니다.
- 2단계로 돌아갑니다.
# 코드
var doSearch = function(array, targetValue) {
var min = 0;
var max = array.length - 1; //24
var guess;
while(min<=max) {
guess = Math.floor((max+min)/2);
println(guess);
if(array[guess]<targetValue) {
min = guess+1;
} else if (array[guess]>targetValue) {
max = guess-1;
} else {
return guess;
}
}
return -1;
};
var primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
var result = doSearch(primes, 97);
println("Found prime at index " + result);
Program.assertEqual(doSearch(primes, 73), 20);
# 점근적 표기법
###알고리즘의 실행 시간
- 입력값의 크기에 따른 알고리즘 실행 시간
- 배열 크기 작을수록 추측 최대 횟수도 줄어든다
- 실행 시간의 성장률(rate of growth): 입력값의 크기에 따라 이 함수가 얼마나 빨리 커지는지
- 6n^2 + 100n + 300이란 알고리즘에서 n값이 커질수록 6n^2는 나머지 합친것보다 훨씬 기하급수적으로 커진다
- an^2가 bn+c보다 크고 n이 커질수록 그 차이가 커지는 n의 값(교차점)은 항상 존재
- 중요하지 않은 항목과 상수 계수를 제거하면 이해 방해하는 불필요한 부분 없어져서 알고리즘의 실행 시간에서 중요한 부분인 성장률 에 집중할 수 있다.
상수 계수와 중요하지 않은 항목 제거했을 때는 **점근적 표기법(asymptotic notation)**을 사용한다. 이는 1. big-Θ 2. big-O 3. big-Ω 3개가 있다.
# 빅 세타 표기법
아래와 같은 선형 검색이 수행되기 위하여 필요한 계산은
c1 * n + c2
이다.
- c1: 루프를 한 번 도는데 드는 시간
- n: 루프 수
- c2: guess=0, 마지막 -1리턴 작업 등 추가 시간.
여기서 상수 인자인 c1과 c2를 안다고 해서 실행 시간이 커지는 비율을 알 순 없음. 배열 크기인 n에 따라 커지지. 이는 n의 빅 세타, 혹은 n의 세타 라고 읽는다.
var doLinearSearch = function(array) {
for (var guess = 0; guess < array.length; guess++) {
if (array[guess] === targetValue) {
return guess; // 찾은 경우
}
}
return -1; // 찾지 못한 경우
}
이진 검색의 실행 시간은 Θ(log n)
.
n이 두배씩 커질때마다 연산 수가 하나씩 늘어나니까(2의 2제곱 = 연산 2번, 2의 3제곱 = 연산 3번)
실제로 상수 인자와 낮은 차원의 항목은 그냥 생랼.
실행 시간이 6n^2 + 100n + 300 마이크로초라면, 빅-세타 표기법에서는 그냥 Θ(n^2)
라고 한다. (계수, 저차원항목, 단위 생략)
# 점근적 표기법 형태의 함수
오름차순으로 정렬된 배열의 최솟값 찾기 => 항상 index 0에 있으므로 알고리즘은 Θ(n^0)
시간에 실행된다. 실제로는 Θ(1)
라고 씀.
점근적 표기법에서 로그의 밑 값은 상수라면 뭐든 무방.
log a n = log b n / log b a
식은 모든 양수인 a, b, n에 대해 성립하기 때문.
그러므로 만일 a, b가 상수라면 log a n과 lo b n은 log b a에 의해서만 달라지고 이는 점근적 표기법에서 무시 가능한 상수값.
무슨말이지?
그러므로 이진검색 최악경우 실행시간은 모든 양수값 a에 대해 Θ(log a b)
라고 할 수 있다.
보통 Θ(log n)
라고 한다.
점근적 표기법을 사용해 알고리즘 분석시 종종 사용하는 함수 순서가 있다.
a < b
(둘 다 상수)라면 Θ(n^a)의 실행시간보다 Θ(n^a)가 빨리 커짐.Θ(log n)
은 모든 양의 상수 a에 대하여Θ(n^a)
보다 천천히 커짐, 그러나Θ(1)
보단 빨리 커짐
Θ(1)
< Θ(log n)
< n
< Θ(n log n)
< Θ(n^2)
< Θ(n^2 log n)
< Θ(n^3)
< Θ(2^n)
그래프 그려보면 좋겠다
# 빅 오 표기법
빅 세타: 실행 시간이 커지는 것을 일정 하한선~상한선 내에서 점근적으로 제한하기 위해. 빅 오: 상한선만 제한하고 싶다
이진검색의 최악경우 실행시간은 Θ(log n)
이지만 모든 경우에서 Θ(log n)
으로 실행된다고 말할 순 없으니, '실행 시간은 최대한 이만큼 커지지만 더 천천히 커질 수 있다(더 빠를 수 있다)'를 말하는건 big-O
모든 경우를 포괄하는 일반적 표현은 이진검색이 O(log n)시간 내에 실행된다라고 표현하는 것이 가장 강력하다.
정확하진 않지만 이진 검색의 상한선은 O(n^2)가 될 수 있다. 하지만 Θ(n^2)는 올바르지 않다.
e.g.
log n
이 O(log 8 n)
이다
== log n
이 O(log 8 n)
의 점근적 상한선을 가지고 있다
== 충분히 큰 n이 있을 경우 어떤 상수 k에 대해 실행 시간이 최대 k * log 8 n 이라는 것이다. (k를 찾을 수 있으면 성립)
== log 8 n
은 log n
의 빅 오가 될 수 있다
5log8n은 log2n보다 항상 크다! (우리는 k=5를 찾았다) 그래서 O(log8n)은 log2n 이 될 수 있다. log8n은 log2n의 빅오가 될 수 있다. n이 짱 크면 log 8 n이 아무리 커봤자 log n보다 클 수 없다
# 빅 오메가 표기법
빅 세타: 알고리즘이 최소한 어느 정도 걸린다 라고 말하고 싶을 때
실행 시간이 Ω(f(n))
라면 n이 충분히 클 때
실행 시간은 어떤 상수 k에 대해 최소 k * f(n)
오메가랑 오랑 세타랑 결국 상/하만 다른거지 같은 값이 나오는거 아닌가?
# 점근적 표기법 퀴즈
# Q1
n^k와 c^n함수 사이의 점근 관계는 무엇일까? (k>=1, c>1)
- n^k == O(C^n)
- n^k == Ω(C^n)
- n^k == Θ(C^n)
# A1
n^k는 다항함수, c^2는 지수함수. 다항함수가 지수함수보다 항상 천천히 증가한다(n의 값이 충분히 클 때).
# Q2
log 2 n
및 log 8 n
함수들 사이의 점근 관계는 무엇일까?
# A2
둘 다 밑은 다르지만 다항함수적 증가를 보인다 (log 2 n이 log 8 n보다 빨리 증가(처음엔 낮음))
근데 log 8 n = 1/log 2 8 * log n 이기 때문에 log 8 n
은 단순히 log n
에 상수를 곱한 것이라는 걸 알 수 있다.
두 함수가 상수 곱의 차이만 날 때 항상 k를 찾아 상한선을 구할 수 있다.
즉 log 2 n은 O(log 8 n), Ω(log 8 n) Θ(log 8 n) 모두에 해당된다.
log n
은O(log 8 n)
이다: (n이 충분히 클 때) log 8 n은 최소한 log n보다 크다log n
은Ω(log 8 n)
이다: (n이 충분히 클 때) log 8 n은 log n보다는 항상 작다log n
은Θ(log 8 n)
이다: (n이 충분히 클 때) log 8 n이 커지는 비율은 log n이다
# Q3
log n ^ log 17
및 log 17 ^ log n
함수들 사이의 점근 관계는 무엇일까?
# A3
모두 밑(2)이 같은 지수함수. log a^b = b * log a 이기 때문에 두 함수는 동일하다.
# 선택 정렬
# 의사 코드
- 가장 작은 카드를 찾아서 첫번째에 있는 카드랑 바꾼다
- 그 다음으로 작은 카드를 찾아서 두번째에 있는 카드랑 바꾼다
- 배열이 정렬될때까지 반복한다
# 분석하기
- swap함수는 매번 3줄의 코드가 수행된다 (상수)
- indexOfMinimun함수는 여기 넘기는 array length만큼 코드가 수행된다.(n번)
- 길이가 8인 array에서 indexOfMinimum 처음 불리면 8번 계산, 두번째는 7번, 6번...
- 연산 회수는 8+7+6+...+1 = 36
- 선택 정렬에 소요되는 총 실행 시간은 3부분
- indexOfMinimum
- 1+2+...+n 이니까 다 더하면 (n+1)(n/2)인데 이는 n^2/2 + n/2 이다. 즉 가장 높은 차수로
Θ(n^2)
- swap
- 상수.
Θ(n)
- 나머지 루프의 selectionSort 함수(다른 함수 실행, 비교 등)
- 상수.
Θ(n)
- 즉, 선택 정렬은
Θ(n^2)
# QUIZ
- 빅 세타를 왜 쓰지? 빅 세타의 정의
- 빅 세타 그래프 그려보기, 순서 말하기
- 빅 오와 빅 세타의 차이점