Which code is Best?
프로그램의 코드를 평가하는데는 3가지의 관점에서 평가할 수 있다.
1.Readable 2.Space Complexity 3.Time Complexity
Readable
즉 가독성이다. 읽기 쉽고 직관적인 코드는 유지보수에 큰 도움이 되고 프로그램의 수명도 길어진다.
Space Complexity = Memory
메모리 공간을 얼마나 사용하는 지에 대한 관점이다.
메모리 공간은 한정되있고 그것을 최대한 효율적으로 사용하는 코드일 수록 효율적인 코드이다.
ps. Memory의 종류 - heap : 변수와 여러 값들을 직접 저장하는 곳, stack : where we keep track of our function calls
Time Complexity = Speed
처리 속도에 대한 관점이다.
처리 속도가 빠를 수록 효율적인 코드라 할 수 있다.
위 3가지를 모두 항상 만족할 수 없겠지만 그 타협점에서 가장 효율적인 코드를 완성하는 것을 목표로 해야한다!
Big O Notation
어떤 처리 과정이 얼마나 효율적인가를 표현하는 코드의 처리 효율성 Index같은 것이다.
Definition
여러가지 복잡도 표기법중에서는 Big O Notation을 가장 많이 사용한다.
Big O Notation은 밑에 표처럼 여러가지로 구분 될 수 있다.
Rules
Big O Notation으로 복잡도를 판단하는 4가지의 룰이 있다.
1. Worst Case
String [] nemo = {"asd", "sdf", "qwe", "nemo", "zxc"};
findNemo(nemo);
public void findNemo(String []array) {
Date date = new Date();
long startD = date.getTime();
for (int i = 0; i < array.length; i++) {
if (array[i] == "nemo") {
System.out.println("Found Nemo!!");
break;
}
}
long endD = date.getTime();
System.out.println("Call to find Nemo = " + (endD - startD));
}
nemo 배열안에 nemo는 4번째에 있어서 for문을 모두 돌지 않고 4번째에서 멈추기 때문에 복잡도는 4라고 할 수 있지만
Notation은 항상 최대로 for문이 실행되었을 경우를 가정해야 한다. 따라서 O(n)이 된다.
2. Remove Constants
public void findNemo(String []array) {
for (int i = 0; i < array.length; i++) {
if (array[i] == "nemo") {
System.out.println("Found Nemo!!");
break;
}
}
for (int i = 0; i < array.length; i++) {
if (array[i] == "nemo") {
System.out.println("Found Nemo!!");
break;
}
}
}
for문을 2번 실행하기 때문에 O(2n)이 맞지만 constant는 지워줘야하는 룰에 의해 O(n)이 된다.
3. Different terms for inputs
public void findNemo(String []array, String []array2) {
for (int i = 0; i < array.length; i++) {
if (array[i] == "nemo") {
System.out.println("Found Nemo!!");
break;
}
}
for (int i = 0; i < array2.length; i++) {
if (array2[i] == "nemo") {
System.out.println("Found Nemo!!");
break;
}
}
}
넣어주는 parameter값이 여러개라면 모두 다른 이름으로 넣어주어야한다.
4. Drop Non Dominants
public void rule4(String []array, String []array2) {
for (int i = 0; i < array.length; i++) {
if (array[i] == "nemo") {
System.out.println("Found Nemo!!");
}
}
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array2.length; j++) {
System.out.println(array[i]+array[j]);
}
}
}
안에 2가지 for가 있다. O(n + n^2)로 표현가능하지만
rule인 drop non dominants에 의하면 O(n^2)가 더 중요하므로 중요한것만 남겨 써야한다.
Space complexity
공간 복잡도의 계산은 밑의 코드와 같이 계산할 수 있다.
/*
* 밖에서 booos의 관점에서는 밖에서 받아온 변수는 메모리에 포함이 안되기 때문에
* booos불러 사용한다면 O(1)의 공간복잡도라고 할 수 있다.
*/
public void booos(int [] n) {
for (int i = 0; i < n.length; i++) {
System.out.println("boooo!!");
}
}
/*
* n개의 배열을 메소드안에서 생성하기 때문에 그만큼의 메모리가 필요하고
* for문을 위한 i 변수의 매모리공간이 또 필요하기 때문에 O(n)의 공간 복잡도라고 할 수 있다.
*/
public String[] makeArrayWithNum(int n) {
String [] array = new String[n]; //여기서 변수를 만들때 메모리를 소모
for (int i = 0; i < array.length; i++) { //iteration을 위한 i 변수에 의한 메모리 소모
array[i] = "hi";
}
return array;
}
더 좋은 프로그램을 만들기 위한 팁
Javascript String의 length Property
자바스크립트에서 String.length는 과연 얼마의 Big O notation을 가질까 한다면
그것은 String의 글자 수에 달렸다고 말할 수 있지만
자바스크립트 언어 자체에 내장된 length라는 것은 String 함수 자체가 가진 length를 그냥 읽어온 것이다.
String함수를 작성하고 실행하여 String객체를 만들때 이미 length라는 property도 작성된다는 얘기.
즉 함수가 아닌 String 함수내의 하나의 property이기 때문의 O(1)을 가진다.(즉, 그냥 변수 값을 가져오는것으로 생각 할 수 있다.)
즉, 각 언어가 어떻게 동작하는지 알아서 기본적으로 내장된 기능들과 여러 api들의 big O notation을 파악할 수 있으면 훨씬 더 좋은 프로그램을 만들 수 있다는 것이다.
Big O notation이 중요한 이유
시간과 메모리 공간은 cost이고 이것을 측정하는 개념인 Big O notation이다.
이것을 항상 염두해두며 최소한으로 줄일 줄 아는 엔지니어야 말로 회사의 황금과 같은 존재.
따라서 쓰이는 개념은 아니지만 회사가 자주 물어보는 주제이며 engineer로서 항상 염두하고 해결하기 위해 노력해야한다.
하지만 3가지의 좋은 프로그램의 조건을 모두 고려해야하므로 그 3가지를 잘 조절해서 가장 효율적인 코드를 짜내야한다.