[자료구조/알고리즘] 코딩 테스트 준비 & 시간복잡도, Greedy, DP

LEE JI YOUNG·2021년 12월 14일
2

Achievement Goals

  • 알고리즘이 무엇인지 설명할 수 있다.
  • 알고리즘 문제를 이해하고 전략을 세울 수 있다.
  • 알고리즘 풀이에 필요한 수도 코드를 작성할 수 있다.
  • 수도 코드에서 세운 전략을 코드로 구현할 수 있다.
  • 내가 구현한 알고리즘을 자바스크립트 언어로 설명할 수 있다.

Prerequisite

  • 기본적인 자료 구조(stack, queue, tree, graph 등)에 대한 이해
  • 모듈 단위로 나누어 생각하는 법

참고할 블로그글


코딩테스트 준비

  • 일반적으로 준비해야하는 단계 : 프로그래머스 기준 레벨 3, 해커링크 기준 medium 정도 난이도.
  • 어렵게 내는 기업(카카오, 구글 (네이버는 약한 편)) : 4문제 중 1문제 꼴로 3.5~4단계, 해커랭크 hard 정도 문제 출제

알고리즘 문제 풀기 단기간에 준비 할 때는 주입식 교육을 통해 알고리즘을 학습하는 것이 효과적이다.

  • 많은 문제 풀고 유형에 대비해야한다.
  • 일부 코딩 테스트는 잘 알려진 유형들만 출제된다. 유형들을 접근하는 템플릿이 존재함.

코딩테스트 트랜드

  • 자신이 생각하는 것을 코딩할 수 있는 것, 알고리즘 응용

대기업가려면...
알고리즘 공부, 컴퓨팅사고, CS(컴퓨터사이언스) 만 1년 공부해서 가기도 한다.

알고리즘 공부 추천 사이트
GeeksforGeeks.org
Lintcode - 대기업 코딩테스트 문제 사이트

  • 알고리즘 이해 순서가 있다.

  • 공부해야 할 알고리즘 : 재귀, Backtracking, Divide and Conquer, Graph Algorithm, Greedy, DP(냅색, 메모이제이션, 동전), Searching, Sorting(주어진 메소드 가장 빠르다(O(n) 안된다.) 쓰기 : arr.sort((a,b) => a-b)) (힙, 머지, 퀵소트 개념, 시간복잡도 등을 물어볼 수 있다.) Ramdomized Algorithm(매우 어려운것으로 코딩테스트에 절대 안나옴)...

  • 기본적으로 알아야하는 유형
    : 이해해서 외우고 코딩테스트 직전에 보고가야한다.

      1. GCD
      1. 순열/조합
      1. 정렬은 Array.prototype.sor 사용
      1. DFS(재귀), BFS(큐)
      1. 분할정복(재귀), DP => 감을 잡기 어렵기 때문에 케이스 스터디 필요 (Geeksforgeeks)
  • 대부분의 코딩테스트는 실행시간 1초에 가깝게 디자인된다.
    (보통 1억번의 연산 당 1초)

  • 시간복잡도 : 우선적 고려.

    시간복잡도1초가 걸리는 입력의 크기
    O(N)100,000,000
    O(NlogN)5,000,000
    O(N^2)10,000
    O(N^3)500
    O(2^N)26
    O(N!)11
  • 공간복잡도 : JS에서 보통 변수 하나는 8Byte. 일반적으로 128/256/512MB 가 자주 등장. 공간복잡도는 메모리성능이 점점 좋아지므로 사실 시간복잡도에 비해 고려도가 낮음.

    • 메모리 조건이 128MB일 때,

  • 문제를 처음 봤을 때

  1. 입력과 공간 상한을 확인.
  2. 먼저, 완전탐색으로 풀어보기. (알고리즘을 알겠다면 바로 풀어도 됨.) 푸는 과정에서 문제 이해하기. 완전탐색 - 무차별 대입해서 문제 푸는 것.
  3. 문제 푸는 시간의 30~50%는 문제를 분석하는 데에만 사용.
  • 알고리즘 문제는 문제 푸는게 중요. 숏코딩은 후차적 문제. 코드 정리, 주석달기는 소규모 코딩테스트에서 면접까지 대비할 경우.

  • 시간이 부족해서 못푸는 것은 노력한 모습이라도 보여주면 좋다.

알고리즘이란?

: 알고리즘은 문제를 해결하는 최선의 선택. 모든 경우의 수를 경험하고 최선의 수를 선택할 수 없기 때문에, 알고리즘을 이용해 문제를 해결해야 한다.

  • 컴퓨터는 어떻게 최선의 수를 찾을 수 있을까요?
  • 컴퓨터는 어떻게 문제를 해결할까요?

문제 푸는 법

  1. 문제 이해
    : 주어진 조건(문제 설명, 입출력예시, 제한사항, 주의사항)을 토대로 문제가 무엇인지를 이해 하기.
  2. 문제 전략세우기
    : 연습장에 전체적인 흐름 그려보고 수도코드 짜기.
  3. 문제를 코드로 옮기기
    : 코드로 옮긴 후, 구현한 코드 최적화 시도.

Notes

  • 개발 태스크는 알고리즘 문제만큼 어렵지 않으나 문제를 전략과 알고리즘을 구상하여 실제로 코드로 구현해 보는 경험은 매우 중요.
  • 많은 기업에서 주니어 개발자를 채용할 때에, 알고리즘 풀이를 통해 지원자의 역량을 가늠. 알고리즘 풀이로 지원자의 로직과 문제해결 방식을 통해 개발자다운 사고방식을 확인.

Time Complexity

시간복잡도

: 입력값의 변화에 따라 연산을 실행할 때, 연산 횟수에 비해 시간이 얼마만큼 걸리는가?

  • 효율적인 방법으로 문제를 해결 했는지에 대한 척도.
  • 시간복잡도 표기 방법
    • Big-O(빅-오) : 최악의 경우 고려
    • Big-Ω(빅-오메가) : 최선의 경우 고려
    • Big-θ(빅-세타) : 평균을 고려

Big-O(빅-오) 표기법 (notation)

: 최악의 경우 시간 고려. 최악을 고려해야 그에 맞는 대응이 가능. 최악의 경우를 고려해야 문제가 발생했을 때 어디에서 문제가 생긴지 알수 있다.

  • Big-O 표기방법 : log base, 계수, 상수항을 제외하고 표현

빅오표기법 종류

1. O(1) = constant complexity
: 입력값이 증가하더라도 시간이 늘어나지 않음.

  • 입력값의 크기(입력값의 갯수)와 관계없이, 즉시 출력값을 얻어낼 수 있다.
// O(1)의 시간 복잡도를 가지는 알고리즘 예시
function O_1_algorithm(arr, index) {
	return arr[index];
}

// - 해당 index에 접근하는 것이므로 arr의 길이가 100만이 되어도 
// 걸리는 시간은 같다. 
let arr = [1, 2, 3, 4, 5];
let index = 1;
let result = O_1_algorithm(arr, index);
console.log(result); // 2

2. O(n) = linear complexity
: 입력값이 증가함에 따라 시간이 같은 비율로 증가하는 것.

  • 입력값의 크기(입력값의 갯수)에 따라 즉시 출력값을 얻는 시간 정비례(같은비율)로 늘어남.
// O(n)의 시간 복잡도를 가지는 알고리즘 예시
// do something for 1 second일 때..
function O_n_algorithm(n) {
	for (let i = 0; i < n; i++) {
	// 입력값(n)이 1 증가할 때마다 코드의 실행 시간이 1초씩 증가
	}
}

function another_O_n_algorithm(n) {
	for (let i = 0; i < 2n; i++) {
	// 입력값이 2배로 늘어나므로, 입력값(n)이 1 증가할 때마다 
        // 코드의 실행 시간이 2초씩 증가.
	}
}
  • O(n)표기법 : 입력값이 커지면 커질수록 계수(n 앞에 있는 수)의 영향력이 없다. 같은 비율로 증가하고 있다면 어떤 상수배수로 증가해도 O(n)으로 표기.
    • O(2n) -> O(n)으로 표기

3. O(log n) = logarithmic complexity
: 한번 연산할 때 마다 n이 (1/2)줄어든다.

  • Big-O표기법중 O(1) 다음으로 빠른 시간 복잡도.
  • O(log n)의 시간 복잡도를 가진 알고리즘(탐색기법) BST(Binary Search Tree) : 원하는 값을 탐색할 때, 노드를 이동할 때마다 경우의 수가 절반(1/2)으로 줄어든다.
    • 예시 ) up & down 게임
      : 1~100중 고른 수 구해내는 최악의 경우의 수 7 => 2^7 > 100
// O(log n)의 시간 복잡도를 가지는 알고리즘 예시
let i = n;
while (parseInt(i) > 0) {
  i = i / 2;
}

// 수학적으론 k배수만큼 i가 커지며 n에 도달하고 있기 때문에 log(base k) n
// log base는 big O notation에서 중요하지 않기때문에 
// O(log k n) -> O(log n)
for (let i = 0; i < n; i++) {
	i *= k;
}

4. O(n^2) = quadratic complexity
: 입력값이 증가함에 따라 시간이 n의 제곱수의 비율로 증가하는 것.

// O(n^2)의 시간 복잡도를 가지는 알고리즘 예시
function O_quadratic_algorithm(n) {
	for (let i = 0; i < n; i++) {
		for (let j = 0; j < n; j++) {
		// do something for 1 second
		}
	}
}

function another_O_quadratic_algorithm(n) {
	for (let i = 0; i < n; i++) {
		for (let j = 0; j < n; j++) {
			for (let k = 0; k < n; k++) {
			// do something for 1 second
			}
		}
	}
}
  • O(n^2)표기법 : n이 커지면 커질수록 지수가 주는 영향력이 점점 퇴색되기 때문에 O(n^2) 표기.
    • O(n^2), O(n^3), O(n^5) -> O(n^2)으로 표기

5. O(2^n) = exponential complexity
: 입력값이 1 증가함에 따라 시간이 2배로 증가하는 것.

  • Big-O 표기법 중 가장 느린 시간 복잡도.
  • O(2^n)의 시간 복잡도를 가진 대표적인 알고리즘
    : 재귀로 구현하는 피보나치 수열.
    : n을 40으로 두어도 수초가 걸린다. n이 100 이상이면 평생 결과를 반환받지 못할 수 있다.
// O(2^n)의 시간 복잡도를 가지는 알고리즘 예시
// 재귀로 구현하는 피보나치 수열
function fibonacci(n) {
	if (n <= 1) {
		return 1;
	}
	return fibonacci(n - 1) + fibonacci(n - 2);
}

코딩테스트에서 시간복잡도 예측하기

  • 데이터 크기에 따른 시간 복잡도
데이터 크기 제한예상되는시간 복잡도
n ≤ 1,000,000O(n) or O (logn)
n ≤ 10,000O(n^2)
n ≤ 500O(n^3)
  • 입력 데이터가 클 때 : O(n) , O(log n)의 시간 복잡도를 만족하도록 풀기.
  • 입력 데이터가 작을 때 : 시간 복잡도가 크더라도 문제 푸는 것에 집중하기.

질문

  • 가장 빠른/느린 시간 복잡도는 무엇일까요?
    : 가장 빠른 O(1), 가장 느린 O(2^n)
    : O(1) < O(logn) < O(n) < O(n^2) < O(2^n) < O(n!)
  • 효율적인 알고리즘을 구성했다 함은 무엇을 의미할까요?
    : 시간 복잡도 적은 알고리즘 구성.
  • 시간 복잡도는 A와 B의 비례함이 어느 정도인지를 나타냅니다. A와 B는 무엇일까요?
    : A(입력값의 크기(연산횟수)), B(실행 시간)

Greedy Algorithm(탐욕 알고리즘)

: 매 순간, 최적이라 생각되는 해답(locally optimal solution)을 찾으며, 이를 토대로 최종 문제의 해답(globally optimal solution)에 도달하는 문제 해결 방식.

  • 탐욕 알고리즘 적용 가능한 문제 조건 2가지 :
    • 탐욕적 선택 속성(Greedy Choice Property)
      : 앞의 선택이 이후의 선택에 영향을 주지 않는다.
    • 최적 부분 구조(Optimal Substructure)
      : 문제에 대한 최종 해결 방법은 부분 문제에 대한 최적 문제 해결 방법으로 구성된다.

: 위의 경우를 제외하면 항상 최적의 결과를 보장하지는 못한다는 점을 명심. 탐욕 알고리즘은 항상 최적의 결과를 도출하는 것은 아니지만, 어느 정도 최적에 근사한 값을 빠르게 도출할 수 있는 장점이 있다. 이 장점으로 인해 탐욕 알고리즘은 근사 알고리즘으로 사용 가능.

탐욕알고리즘 문제 해결 단계

  1. 선택 절차(Selection Procedure)
    : 현재 상태에서의 최적의 해답을 선택.
  2. 적절성 검사(Feasibility Check)
    : 선택된 해가 문제의 조건을 만족하는지 검사.
  3. 해답 검사(Solution Check)
    : 문제가 해결되었는지 검사 후 해결되지 않았다면 1로 돌아가 반복.
  • 적용 예시 ) 거스름돈 960원을 동전의 개수를 최소한으로 돌려주기
    1. 선택 절차 : 거스름돈의 동전 개수를 줄이기 위해 현재 가장 가치가 큰 동전을 우선 선택.
    2. 적절성 검사 : 1번 과정을 통해 선택된 동전들의 합이 거슬러 줄 금액을 초과하는지 검사. -> 초과하면 가장 마지막에 선택한 동전을 삭제하고, 1번으로 돌아가 한 단계 작은 동전을 선택.
    3. 해답 검사 : 선택된 동전들의 합이 거슬러 줄 금액과 일치하는지 검사. 액수가 부족하면 1번 과정부터 다시 반복.
  • 적용이 안되는 예시 ) 배낭 짐 싸기 문제
    : 35kg까지의 물건만 담을 수 있는 배낭에 가장 비싼 물건 담기.
    (그림 : $3000-30kg , tv : $2500-25kg, 모니터 : $2000-20kg, 반지 : $1500-15kg)
    1. 가방에 넣을 수 있는 물건 중 가장 비싼 물건을 넣는다.
    2. 그다음으로 넣을 수 있는 물건 중 가장 비싼 물건을 넣는다. 이 과정을 반복.
      -> 최적으로 가장 비싼 물건인 그림($3000)을 고르면 그림만 담을 수 있다.
      -> 모니터 + 반지 = $3500를
      배낭 짐 싸기 예와 같이 항상 최적의 결과를 보장하지는 못한다는 점을 명심. 탐욕적 선택 속성(앞에 선택이 뒤에 영향 X), 최적 부분 구조(최종문제해결방법은 부분문제에 대한 최적문제해결의 모음)

Implementation (이행)

  • 알고리즘 문제를 푼다 = 내가 생각한 문제 해결 과정을 컴퓨팅 사고로 변환하여 코드로 구현한다.
  • 문제 해결 과정은 대부분 여러 개의 카테고리로 묶임.
    : 예 ) N 개의 숫자들을 특정한 기준에 맞게 순서를 조정하는 것 - 정렬
    : 예 ) 그래프 탐색의 경우 탐색 - 방식에 따라 DFS, BFS
    • 데이터를 정렬할 수 있는가?
    • 데이터를 효율적으로 탐색할 수 있는가?
    • 데이터를 조합할 수 있는가? ...etc
  • 문제 해결을 코드로 구현이 정확하고 빠를 수록 좋은 개발자.
    • 본인이 선택한 프로그래밍 언어의 문법을 정확히 알기.
    • 문제의 조건에 전부 부합하는 코드를 실수 없이 빠르게 작성하기.
  • 구현 능력 test case
    • 완전 탐색(brute force) : 가능한 모든 경우의 수를 전부 확인하여 문제를 푸는 방식.(하드코딩)
    • 시뮬레이션(simulation) : 문제에서 요구하는 복잡한 구현 요구 사항을 하나도 빠트리지 않고 코드로 옮겨, 마치 시뮬레이션을 하는 것과 동일한 모습을 만들기.

1. 완전 탐색

: 방법은 굉장히 단순하고 무식하지만 "답이 무조건 있다"는 강력함. 단순히 모든 경우의 수를 탐색하는 모든 경우를 통칭.

  • 완전 탐색은 문제를 해결할 수 있는가? O
  • 완전 탐색은 효율적으로 동작하는가? X
  • 완전 탐색 방법
    • brute Force(조건/반복을 사용하여 해결): 무차별 대입
    • 재귀
    • 순열
    • DFS/BFS 등 여러 가지가 있습니다.

2. 시뮬레이션

: 시뮬레이션은 모든 과정과 조건이 제시되어, 그 과정을 거친 결과가 무엇인지 확인하는 유형.

  • 문제가 길고 자세하여 코드로 옮기는 작업이 까다로움.
  • 문제에 대한 이해를 바탕으로 제시하는 조건을 하나도 빠짐없이 처리해야 정답이 가능.
  • 하나라도 놓친다면 통과할 수 없게 되고, 코드가 길어 헷갈릴 수도 있으니 주의해야 함.


Dynamic Programming (DP; 동적 계획법)

: 주어진 문제를 여러 개의 하위 문제로 나누어 풀고, 하위 문제들의 해결 방법을 결합하여 최종 문제를 해결하는 문제 해결 방식. 하위 문제를 계산한 뒤 그 해결책을 저장하고, 나중에 동일한 하위 문제를 만날 경우 저장된 해결책을 적용해 계산 횟수를 줄인다. 즉, 하나의 문제는 단 한 번만 풀도록 하는 알고리즘.

  • 탐욕 알고리즘과 함께 언급하는 알고리즘.

    • 같은 점 : 탐욕 알고리즘과 같이 작은 문제에서부터 출발한다는 점은 같다.
    • 다른 점 : 탐욕 알고리즘은 매 순간 최적의 선택을 찾는 방식, DP는 모든 경우의 수를 조합해 최적의 해법을 찾는 방식.
  • 다이내믹 프로그래밍 적용 가능한 문제 조건 2가지 :

    • Overlapping Sub-problems(겹치는 부분 문제)
      : 큰 문제를 작은 문제로 나눌 수 있고, 이 작은 문제가 중복해서 발견된다. 즉, 큰 문제로부터 나누어진 작은 문제는 큰 문제를 해결할 때 여러 번 반복해서 사용될 수 있어야 한다.
      • ex ) 재귀함수로 구현한 피보나치 수열

        : 작은 문제의 결과를 큰 문제를 해결하기 위해 여러 번 반복하여 사용할 수 있을 때, 부분 문제의 반복해서 사용하지만, 주어진 문제를 단순히 반복 계산하여 해결하는 것이 아니라, 작은 문제의 결과가 큰 문제를 해결하는 데에 여러 번 사용될 수 있어야 한다. 재귀함수로 구현한 피보나치는 여러번 계산을 반복하게 한다.
    • Optimal Substructure(최적 부분 구조) (탐욕알고리즘과 같은 조건)
      : 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 같다. 즉, 작은 문제에서 구한 정답을 큰 문제에서도 사용 가능하다. 주어진 문제에 대한 최적의 해법을 구할 때, 주어진 문제의 작은 문제들의 최적의 해법을 찾아야 한다. 작은 문제의 해볍을 결합하면 문제의 최적의 해법을 구할 수 있다.
      • ex ) A에서 D로 가는 최단 경로를 찾는 문제
        A에서 D로 가는 최단 경로 = A → B → C → D
        A에서 C로 가는 최단 경로 = A → B → C
        A에서 B로 가는 최단 경로 = A → B
        : 작은 문제의 최적 해법을 결합하여 최종 문제의 최적 해법을 구할 수 있다.

DP : Recursion + Memoization

: 재귀를 이용한 DP(Memoization) 구현

  • Memoization : 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술.
    • DP는 하위 문제의 해결책을 저장한 뒤, 동일한 하위 문제가 나왔을 경우 저장해 놓은 해결책을 이용한다. 이때 결과를 저장하는 방법이 메모이제이션이다.
  • Top-down 방식 : 큰 문제를 해결하기 위해 작은 문제를 호출하는 방식.
    • DP을 적용한 피보나치 수열에서 fib(7)을 구하기 위해 fib(6)을, fib(6)을 구하기 위해 fib(5)을 호출한다.

재귀 + DP(Memoization)로 구현한 피보나치 수열

function fibMemo(n, memo = []) {
		// 이미 해결한 하위 문제인지 찾아본다
    if(memo[n] !== undefined) return memo[n];
    if(n <= 2) return 1;
		// 없다면 재귀로 결괏값을 도출하여 res 에 할당
    let res = fibMemo(n-1, memo) + fibMemo(n-2, memo);
		// 추후 동일한 문제를 만났을 때 사용하기 위해 리턴 전에 memo 에 저장
    memo[n] = res;
    return res;
}
  1. 먼저 fibMemo 함수의 파라미터로 n 과 빈 배열을 전달. 이 빈 배열은 하위 문제의 결과값을 저장하는 데에 사용.
  2. memo 라는 빈 배열의 n번째 인덱스가 undefined 이 아니라면(n 번째 인덱스에 어떤 값이 저장되어 있다면) 저장되어 있는 값을 그대로 사용.
  3. undefined라면(처음 계산하는 수라면) fibMemo(n-1, memo) + fibMemo(n-2, memo)를 이용하여 값을 계산하고, 그 결과값을 res 라는 변수에 할당.
  4. res 를 리턴하기 전, memo 의 n 번째 인덱스에 res 값을 저장. 이렇게 하면 (n+1)번째의 값을 구하고 싶을 때, n번째 값을 memo 에서 확인해 사용가능.
  • Memoization을 적용한 피보나치 수열 모식도

    : fib(7) 을 구하기 위해서는 이전의 작업으로 저장해 놓은 하위 문제의 결과값을 사용. n이 커질수록 계산해야 할 과정은 선형으로 늘어나서 시간 복잡도는 O(N).
    • Memorization을 사용하지 않고 재귀 함수로만 문제를 풀 경우 : n이 커질수록 계산해야 할 과정이 두 배씩 늘어나 시간 복잡도가 O(2^N).
    • 시간복잡도는 다이내믹 프로그래밍의 강점을 보여줌.

DP : Iteration + Tabulation

: 반복문을 이용한 DP 구현

  • Bottom-up 방식 : 반복문을 이용한 방법은 작은 문제에서부터 시작하여 큰 문제를 해결해 나가는 방법.

반복문 + DP(Tabulation)로 구현한 피보나치 수열

function fibTab(n) {
    if(n <= 2) return 1;
    let fibNum = [0, 1, 1];
		// n 이 1 & 2일 때의 값을 미리 배열에 저장해 놓는다
    for(let i = 3; i <= n; i++) {
        fibNum[i] = fibNum[i-1] + fibNum[i-2];
		// n >= 3 부터는 앞서 배열에 저장해 놓은 값들을 이용하여
		// n번째 피보나치 수를 구한 뒤 배열에 저장 후 리턴한다 
    }
    return fibNum[n];
}
  • 피보나치 수열을 3가지 방법
    • 재귀 : O(2^N)
    • 재귀 + memoization : O(N)
    • 반복 + tabulation : O(N)

질문

  • Top-down과 Bottom-up의 소요 시간을 비교하였을 때 결과에 어떤 차이가 있고, 그 원인은 무엇이었을까요?
    : Top-down 방식은 점화식을 이해하기 쉽다는 장점이 있고, Bottom-up 방식은 함수를 재귀 호출하지 않기 때문에 시간과 메모리 사용량을 줄일 수 있다는 장점.
    두 방법 중 어느 것이 시간적으로 더 효율이 있는지 묻는 데 그 답은 '알수 없다'이다. 실제로 재귀는 내부 스택을 만들고 함수를 호출하는 과정이 더 있어보여서 반복이 더 빠를 것 같다고 느낄 수 있다. 하지만, Top-Down을 통해 문제를 풀어가는 경우에는 점화식에 따라 불 필요한 계산을 오히려 Bottom-Up보다 덜하는 경우가 있기 때문에 궁극적으로는 알 수 없다.
    • Top-down은 가장 큰 문제를 방문 후 작은 문제를 호출 하여 답을 찾는 방식
    • Bottom-up은 가장 작은 문제들 부터 답을 구해가며 전체 문제의 답을 찾는 방식
  • 다이내믹 프로그래밍과 탐욕 알고리즘은 각각 어떤 경우에 사용하기 적합한 알고리즘일까요?

크롬 개발자 도구에서 함수 실행 시간 측정 방법

함수의 실행 시간을 측정하는 방법은 여러 가지가 있습니다. 그중에서 다음의 방법으로 간단하게 함수의 실행 시간을 확인할 수 있습니다. 실행 환경에 따라 결과가 다르므로 측정 결과는 학습 용도로만 사용하세요.

// < 함수의 실행 시간을 측정하는 방법 >

var t0 = performance.now();
fib(50); // 여기에서 함수 실행을 시켜주세요
var t1 = performance.now();
console.log("runtime: " + (t1 - t0) + 'ms')
profile
프론트엔드 개발자

0개의 댓글