하나의 문제에 대해 많은 해결방안이 나온다.
이 해결방안중에서도 좋은 해결방안과 그저그런 해결방안이 있을것이다.
즉, 어떤 해결방안이 좋은지 비교하기 위해서 Big O가 탄생하였다.
코드를 분류하거나 비교할 수 있는 시스템이다.
나의 코드가 어느 분류에 들어갈 수 있는지 Big O를 통해 확인할 수 있다.
n이 주어지면 1부터 n까지 모든 합을 구하는 문제이다.
#1
n = 100000000;
total = 0;
for(let i=1; i<=n; i++){
total += i;
}
console.log(total);
결과값
5000000050000000
[Done] exited with code=0 in 1.057 seconds
#2
n = 100000000000;
total = 0;
total = n * (n+1) /2;
console.log(total);
결과값
5000000050000000
[Done] exited with code=0 in 0.077 seconds
#1의 코드는 1.057초가 걸리고 #2의 코드는 0.077초가 걸린다.
이렇게 속도로 비교할 수 있지만, 시간이 별로 차이가 나지않아 어느정도로 차이가 나는지 가늠하기가 어렵다.
우리의 코드를 보고 시간을 측정하지 않고도 어느 코드가 더 좋은 코드인지 평가할 수 있을까?
이럴때 Big O
표기법이 유용하다.
컴퓨터의 연산갯수를 비교한다.
아까의 코드를 살펴보며 이해해보자.
#2
#2의 코드는 연산이 *,+,/로 총 3개가 있다.
n의 크기와 상관없이 연산은 무조건 3번 이루어진다.
#1
#1의 코드는 어떨까? +가 있으니 연산은 총 1번일까?
for문은 n만큼 반복한다. 그러므로 #1의 연산은 총 n번 실행되는 것이다.
즉 n의 갯수가 늘어날수록 연산이 커진다.
Big O의 표기는 실행시간이 갖을 수 있는 최대치이다.
f(n)=1
이며 O(1)
이라고 표시한다.f(n)=n
이며 O(n)
이라고 표시한다.f(n)=n^2(제곱)
이며 O(n^2)
이라고 표시한다.뿐만아니라 n에따라 완전히 다른 표기법이 나타날 수도있다.
function countUpAndDown(n) {
console.log("Going up!");
for (var i = 0; i < n; i++) {
console.log(i);
}
console.log("At the top!\nGoing down...");
for (var j = n - 1; j >= 0; j--) {
console.log(j);
}
console.log("Back down. Bye!");
결과값
Going up!
0
1
2
3
4
5
6
7
8
At the top!
Going down...
8
7
6
5
4
3
2
1
0
Back down. Bye!
}
이와같이 for문이 2개면 Big O표기법으로는 O(2n)일까?
다음의 그래프는 n에 100,1000,10000을 입력했을때 속도의 그래프이다.
결국 n만큼 연산하므로 정비례 그래프를 나타내는것을 알 수 있다.
그러므로 for문이 두개여도 O(n)
으로 나타낸다.
그러나 위와같이 for문이 중첩으로 되어 있으면 n의 제곱이다.
첫번째 for문의 i가 오고 j가 다 돌고 나서야 다음 i로 넘어갈 수 있기 때문이다.
그러므로 중첩이 있으면 Big O는 O(n^2)
이다.
위와같이 작업시간이 기하급수적으로 늘어나는 것을 볼 수 있다.
O(1)
O(n)
이 된다.O(n)
이 된다.
위의 코드에서 공간복잡도는 O(1)이다.
total은 arr[i]를 계속해서 더하기는 하지만 결국 숫자이므로 숫자의 공간복잡도는 무조건 O(1)이다.
위의 코드에서 공간복잡도는 O(n)이다.
newArr에게 push를 해주므로 계속해서 배열의 길이가 늘어나기 때문이다.