티스토리 뷰
문제를 풀고 제출했는데 시간 초과! 완전 탐색을 하기 위해 재귀를 이용하긴 했지만 꼭 필요한 경우만 돌았는데도 시간 초과라 의아했다. 완전 탐색이 아닌 다른 방법으로 푸는 방법도 있겠지만 Math.max() 메서드 사용이 시간 지연의 원인일 수도 있겠다는 생각이 들었다. 대충 검색을 해보니 Math.max()의 성능이 그리 좋지 않다는 것을 확인할 수 있었다. 그래서 삼항 연산자, 비교 연산자, Math.max()를 이용한 최댓값 구하기의 속도를 비교해 보았다.
public class Test {
public static void main(String[] args) {
long start = 0, end = 0;
long[] time = new long[4];
int fastest = 0;
int[] arr = new int[100_000_000];;
int max = 0;
System.out.println("CASE :");
System.out.println("1. 삼항 연산자를 사용한 경우1의 소요 시간 (max = (max <= arr[i]) ? arr[i] : max;)");
System.out.println("2. 삼항 연산자를 사용한 경우2의 소요 시간 (max = (max >= arr[i]) ? max : arr[i];)");
System.out.println("3. 비교 연산자를 사용한 경우의 소요 시간 (if(max < arr[i]) max = arr[i];)");
System.out.println("4. Math.max()를 사용한 경우의 소요 시간 (내부적으로 삼항연산자 사용)");
System.out.println();
System.out.println(" 1 2 3 4 Fastest Case % to case 4");
System.out.println();
for (int k = 0; k < 30; k++) {
fastest = 0;
System.out.print("Test#" + (k+1) + " ");
for (int i = 0; i < arr.length; i++)
arr[i] = ((int) (Math.random() * Integer.MAX_VALUE) + 1);
// CASE 1:
max = 0;
start = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++)
max = (max <= arr[i] ? arr[i] : max);
end = System.currentTimeMillis();
time[0] = end - start;
System.out.print(time[0] + " ");
// CASE 2:
max = 0;
start = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++)
max = (max >= arr[i] ? max : arr[i]);
end = System.currentTimeMillis();
time[1] = end - start;
System.out.print(time[1] + " ");
// CASE 3:
max = 0;
start = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++)
if (max < arr[i])
max = arr[i];
end = System.currentTimeMillis();
time[2] = end - start;
System.out.print(time[2] + " ");
// CASE 4:
max = 0;
start = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
max = Math.max(arr[i], max);
}
end = System.currentTimeMillis();
time[3] = end - start;
System.out.print(time[3] + " ");
// 가장 빠른 경우 찾기
for (int i = 1; i < time.length; i++) {
if(time[i] < time[fastest])
fastest = i;
}
System.out.print((fastest+1)+" ");
System.out.printf(" %d %%", (int)(100.0 * time[fastest] / time[3]));
System.out.println();
}
}
}
삼항 연산자에서 대입 값의 위치에 따라 차이가 있는지 궁금해서 CASE 1과 CASE 2로 비교하고, 비교 연산자를 이용하는 경우(CASE 3), Math.max() 메서드를 이용하는 경우(CASE 4)로 두고 테스트를 해보았다. 테스트 케이스마다 배열에 1억 개의 값을 생성해 비교했다.
CASE :
1. 삼항 연산자를 사용한 경우1의 소요 시간 (max = (max <= arr[i]) ? arr[i] : max;)
2. 삼항 연산자를 사용한 경우2의 소요 시간 (max = (max >= arr[i]) ? max : arr[i];)
3. 비교 연산자를 사용한 경우의 소요 시간 (if(max < arr[i]) max = arr[i];)
4. Math.max()를 사용한 경우의 소요 시간 (내부적으로 삼항연산자 사용)
1 2 3 4 Fastest Case % to case 4
Test#1 250 321 328 438 1 57 %
Test#2 297 399 429 404 1 73 %
Test#3 219 276 348 501 1 43 %
Test#4 234 242 234 437 1 53 %
Test#5 219 252 234 391 1 56 %
Test#6 219 237 235 375 1 58 %
Test#7 234 254 219 390 3 56 %
Test#8 235 252 234 375 3 62 %
Test#9 235 243 235 390 1 60 %
Test#10 228 247 225 391 3 57 %
Test#11 250 238 236 399 3 59 %
Test#12 220 246 235 390 1 56 %
Test#13 469 363 250 391 3 63 %
Test#14 388 476 269 391 3 68 %
Test#15 344 408 483 416 1 82 %
Test#16 218 314 391 516 1 42 %
Test#17 219 251 250 485 1 45 %
Test#18 234 241 234 375 1 62 %
Test#19 218 247 234 375 1 58 %
Test#20 235 238 235 375 1 62 %
Test#21 219 238 234 391 1 56 %
Test#22 250 238 234 375 3 62 %
Test#23 218 250 235 390 1 55 %
Test#24 234 236 234 391 1 59 %
Test#25 234 242 234 391 1 59 %
Test#26 317 252 223 390 3 57 %
Test#27 313 330 291 390 3 74 %
Test#28 282 347 377 405 1 69 %
Test#29 234 305 344 434 1 53 %
Test#30 235 241 234 437 3 53 %
결과는 1 < 3 < 2 <<< 4의 순서로 시간이 오래 걸렸다. CASE 1과 CASE 2를 비교했을 때 거의 매번 CASE 1의 경우가 빨랐지만 조건문에서 분기해서 대입 값을 결정하고 해당 값을 대입하는 데 어떤 점이 다른지 모르겠다. (값이 유사할 때 비트 전환이 더 빠르다고 하면 모를까) 테스트 전 내 예상은 'CASE 3가 가장 빠를 것이다'였다. 그 이유는 조건문에서 값을 비교하는 상황은 같지만 false 인 경우 대입 과정이 수행되지 않기 때문이었다. 하지만 CASE 3 보다 CASE 1이 빠른 경우가 많았다.
1 2 3 4 Fastest Case % to case 4
Test#1 188 245 250 422 1 44 %
Test#2 219 240 219 391 1 56 %
Test#3 242 255 224 415 3 53 %
Test#4 284 262 260 555 3 46 %
Test#5 295 263 242 547 3 44 %
Test#6 219 241 476 1014 1 21 %
Test#7 219 233 282 437 1 50 %
Test#8 242 321 235 518 3 45 %
Test#9 225 247 218 375 3 58 %
Test#10 227 262 240 394 1 57 %
Test#11 219 232 219 391 1 56 %
Test#12 250 238 235 390 3 60 %
Test#13 219 237 219 391 1 56 %
Test#14 329 498 250 406 3 61 %
Test#15 234 331 259 394 1 59 %
Test#16 411 376 360 437 3 82 %
Test#17 360 350 453 406 2 86 %
Test#18 268 358 301 394 1 68 %
Test#19 406 520 320 385 3 83 %
Test#20 250 322 312 391 1 63 %
Test#21 241 237 219 419 3 52 %
Test#22 219 227 225 390 1 56 %
Test#23 267 238 219 371 3 59 %
Test#24 266 253 219 374 3 58 %
Test#25 219 249 208 390 3 53 %
Test#26 223 247 219 375 3 58 %
Test#27 250 237 238 372 2 63 %
Test#28 261 254 223 393 3 56 %
Test#29 344 332 273 406 3 67 %
Test#30 328 328 263 399 3 65 %
한 번 더 돌려보았다. CASE 3가 가장 빠른 경우가 늘었고, CASE 2가 가장 빠른 경우가 등장했다. 결론은 CASE 1, 2, 3는 큰 차이가 있다고 보기는 어려울 것 같고, 매번 Math.max() 함수를 호출하는 CASE 4는 확실히 느리다. 코드가 깔끔해 보이고 왠지 효율적으로 구현되어있을 것 같아서 생각 없이 사용해왔던 것을 반성해본다.
코딩테스트 시간이 종료되어 변경된 코드를 실행해보진 못했지만, 오늘도 하나 배웠다.
'개발 > 알고리즘' 카테고리의 다른 글
[Codility] Find the Longest Substring without Repeating Characters Three Times in a Row (0) | 2019.10.05 |
---|---|
[구름] 사탕 받기 카드 게임 (0) | 2019.09.29 |
[백준] 회전하는 큐(1021) (0) | 2019.09.27 |
[구름] 전광판 광고 (0) | 2019.09.24 |
[프로그래머스] 스택/큐 - 프린터(42587) (0) | 2019.09.20 |