algorithm

πŸ—ƒοΈ μ•Œκ³ λ¦¬μ¦˜ | μž¬κ·€ ν•¨μˆ˜(Recursion), ν”Όλ³΄λ‚˜μΉ˜ 수

grasinnong 2020. 7. 23. 18:26

 

 

what is μž¬κ·€ ν•¨μˆ˜

λ§Žμ€ μ‚¬λžŒλ“€μ΄ λ§›μ§‘μ—μ„œ λ°₯을 λ¨ΉκΈ° μœ„ν•΄ 쀄을 μ„°λ‹€. ν˜„μž¬ λ‚˜λŠ” μ€„μ˜ 맨 끝에 μœ„μΉ˜ν•˜κ³  μžˆλ‹€. ν˜„μž¬ λ‚΄κ°€ λͺ‡ 번째둜 λŒ€κΈ°ν•˜κ³  μžˆλŠ”μ§€ μ•Œκ³  μ‹Άλ‹€λ©΄ μ•ž μ‚¬λžŒμ—κ²Œ λͺ‡ λ²ˆμ§ΈμΈμ§€ 물어보면 λœλ‹€. λ§Œμ•½ μ•ž μ‚¬λžŒμ΄ λͺ¨λ₯Έλ‹€λ©΄ μ•ž μ‚¬λžŒμ€ μžμ‹ μ˜ μ•ž μ‚¬λžŒμ—κ²Œ 물어보고 이 과정이 반볡될 것이닀(첫 번째 λŒ€κΈ°μžκΉŒμ§€).

 

 

λ‚˜λŠ” μ•ž μ‚¬λžŒμ—κ²Œ 'μ €λŠ” 5λ²ˆμ΄μ—μš”' λΌλŠ” λŒ€λ‹΅μ„ λ“€μ—ˆλ‹€. κ·Έλ ‡λ‹€λ©΄ λ‚˜μ˜ λŒ€κΈ°λ²ˆν˜ΈλŠ”  5 + 1 = 6번 이닀. 

 

κ·Έ 과정을 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€. 

function getMyPositionInLine(person){
  if (person.nextInLine == null){
  //μ•žμ‚¬λžŒ 쑴재 x
    return 1;
  }
  
  return 1 + getMyPositionInLine(person.nextInLine);
}

 

μ΄λ ‡κ²Œ ν•¨μˆ˜ μ•ˆμ—μ„œ 자기 μžμ‹ (ν•¨μˆ˜)을 λ‹€μ‹œ ν˜ΈμΆœν•˜λŠ” ν•¨μˆ˜λ₯Ό μž¬κ·€ ν•¨μˆ˜λΌκ³  ν•œλ‹€. 

 

 

 

 

μž¬κ·€ ν•¨μˆ˜ ꡬ쑰 

μž¬κ·€ ν•¨μˆ˜μ—μ„œλŠ” 쑰건문이 μ€‘μš”ν•˜λ‹€. 쑰건문 μ•ˆμ—λŠ” μž¬κ·€ ν•¨μˆ˜μ˜ μ’…λ£Œ 쑰건을 λ„£μ–΄μ£Όμ–΄μ•Ό ν•œλ‹€. 

 

 

 

 

 

 

 

 

why we use μž¬κ·€ ν•¨μˆ˜

μž¬κ·€ ν•¨μˆ˜λŠ” for λ¬Έκ³Ό while 문으둜 λŒ€μ²΄κ°€ κ°€λŠ₯ν•˜λ‹€. 

function recursion(someValue){
  while(someValue < 10){
    someValue += 1;
  }
  
  return ;
}

 

ν•˜μ§€λ§Œ μ•„λž˜μ™€ 같이 λ°°μ—΄ μ•ˆμ— 배열이 λ“€μ–΄μžˆκ³  λ˜‘κ°™μ€ 둜직이 λ°˜λ³΅λ˜λŠ” κ²½μš°μ—λŠ” for 문이 계속 쀑첩될 수 μžˆλ‹€.

function recursionArr(array){
  let sum = 0;
  for(let i = 0; i < array.length; i++){
  //λ§Œμ•½ μš”μ†Œκ°€ 배열이라면 
    if (type of array[i] === 'array'){
      for (let j = 0; j < array[i].length; j++){
        //λ§Œμ•½ μš”μ†Œκ°€ 배열이라면 
        if (type of array[i] === 'array'){
         //... 반볡
        }
      }
    }
    else {
      sum += array[i];
    }
  }
  
  return sum;
}


recursionArr([1, [1,3, [1,2,4]]]);

 

 

μž¬κ·€ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  경우, μ½”λ“œλ₯Ό 더 μ§κ΄€μ μœΌλ‘œ κ°„κ²°ν•˜κ²Œ μ§€ 수 μžˆλ‹€. 

function recursionArr(arr, sum){
  for (let i = 0; i < arr.length; i++){
    if (typeof arr[i] === 'number'){
      sum += array[i];
    } else {
      return recursionArr(array[i], sum);
    }
  }
  
  return sum;
}


recursionArr([1, [1,3, [1,2,4]]], 0);

 

 

 

 

 

 

 

 

 

Call Stack

 

콜 μŠ€νƒμ˜ μž‘λ™ 과정은 λ‹€μŒκ³Ό κ°™λ‹€.

function a(){
  return "hello " + b();
};

function b(){
  return "my " + c();
};


function c(){
  return "friends.";
};

 

 

1. call stack 에 "hello " + b() μž‘μ—…μ΄ λ“€μ–΄κ°„λ‹€.

2. ν•¨μˆ˜ aμ—μ„œλŠ” ν•¨μˆ˜ bλ₯Ό ν˜ΈμΆœν•˜κ³  있기 λ•Œλ¬Έμ— "my " + c() μž‘μ—…μ΄ λ“€μ–΄κ°„λ‹€. 

3. ν•¨μˆ˜ bμ—μ„œλŠ” ν•¨μˆ˜ cλ₯Ό ν˜ΈμΆœν•˜κ³  있기 λ•Œλ¬Έμ— "friends ." μž‘μ—…μ΄ λ“€μ–΄κ°„λ‹€. 

4. μž‘μ—…λ“€μ΄ μ‹€ν–‰λ˜λ©΄μ„œ μž‘μ—…λ“€μ΄ call stackμ—μ„œ popλœλ‹€(return). 

 

 

 

μž¬κ·€ ν•¨μˆ˜μ—μ„œ 콜 μŠ€νƒμ€ λ‹€μŒκ³Ό κ°™λ‹€. 

function a(){
  return a();
}

 

μ’…λ£Œ 쑰건이 없을 경우 call stackμ—λŠ” 계속 ν•¨μˆ˜ a()κ°€ ν˜ΈμΆœλ˜μ–΄ μŒ“μ΄κ²Œ λœλ‹€. 콜 μŠ€νƒμ΄ λ„˜μΉ˜κ²Œ 되면 λ©”λͺ¨λ¦¬ λΆ€μ‘±μœΌλ‘œ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€(stack overflow!). 

 

 

 

 

 

 

 

 

꼬리 μž¬κ·€ μ΅œμ ν™”(Tail Recursion)

μž¬κ·€ ν•¨μˆ˜μ˜ 경우, λ°˜λ³΅λ¬Έμ— λΉ„ν•΄ μ‹œκ°„λ„ 였래 걸리고, μŠ€νƒμ΄ λ„˜μΉ˜λŠ” κ²½μš°κ°€ 생긴닀. μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ§Žμ€ 언어듀은 꼬리 μž¬κ·€ μ΅œμ ν™”λΌλŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€. 

 

꼬리 μž¬κ·€ μ΅œμ ν™”λŠ” μ„ ν˜• μ•Œκ³ λ¦¬μ¦˜μ„ λ§Œλ“€μ–΄ μ‹€ν–‰ν•˜κ²Œ ν•œλ‹€. μ„ ν˜• μ•Œκ³ λ¦¬μ¦˜μ€ μ‹œκ°„λ³΅μž‘λ„κ°€ O(n)인 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. λ•Œλ¬Έμ— μ½”λ“œκ°€ 계속 μΆ”κ°€λ˜λ”λΌλ„ μ΅œλŒ€ nλ²ˆμ„ λ°˜λ³΅ν•˜κ²Œ λœλ‹€.

 

https://www.bigocheatsheet.com/

 

 

 

 

 

 

 

 

 

 

 

 

ν”Όλ³΄λ‚˜μΉ˜ 수(Fibonacci numbers)

ν”Όλ³΄λ‚˜μΉ˜ μˆ˜λŠ” F(0) = 0, F(1) = 1이고, n이 2 이상 μΌλ•ŒλŠ” F(n) = F(n-1) + F(n-2) 인 μˆ˜μ—΄μ΄λ‹€. 

1, 1, 2, 3, 5, 8, ....

 

 

μž¬κ·€ ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ n 번째 ν”Όλ³΄λ‚˜μΉ˜ 수λ₯Ό 좜λ ₯ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄ 보자.

 

1. Recursion

function fib(n){
let result = 0;
  if (n === 1 && n === 2){
    result = 1;
  } else if (n >= 2){
    result = fib(n-1) + fib(n-2);
  }
  return result;
};

 

 

5번째 ν”Όλ³΄λ‚˜μΉ˜ 수λ₯Ό κ΅¬ν•˜λŠ” κ²½μš°λŠ” 별 λ¬Έμ œκ°€ μ—†λ‹€κ³  ν•˜λ”λΌλ„, λ§Œμ•½ n이 100이면 fibν•¨μˆ˜κ°€ μ•„μ£Ό 많이 call stack에 μŒ“μ΄κ²Œ 될 것이닀. 

 

ν”Όλ³΄λ‚˜μΉ˜ 수λ₯Ό ꡬ할 λ•Œ μž¬κ·€ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” 경우, μ‹œκ°„λ³΅μž‘λ„λŠ” O(2^n) 이 λœλ‹€.

https://www.bigocheatsheet.com/

 

 

 

 

 

이외에도 ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄μ„ ν‘ΈλŠ” 방법이 μ—¬λŸ¬κ°€μ§€ μžˆλ‹€. 

 

 

 

2. Memoized solution(store)

 

 

fib(5) 값을 ꡬ할 λ•Œ,  fib(3)이 반볡적으둜 ν˜ΈμΆœλœλ‹€. store 방식은 처음 fib(3)κ°€ ν˜ΈμΆœλ˜μ—ˆμ„ λ•Œ, 값을 κΈ°μ–΅ν•œ λ‹€μŒ λ‘λ²ˆμ§Έ fib(3)을 ꡬ할 λ•Œ κ·Έ 값을 μ‚¬μš©ν•œλ‹€.

 

function fib(n,memo){
  if(memo[n] !== null){
    return memo[n];
  } 
  
  if (n === 1 || n === 2){
    result = 1;
  } else {
    result = fib(n-1) + fib(n-2);
    memo[n] = (result);
  }
  return result;
}

 

f (1) = 1, f(2) = 1, f(3) = 2... 값듀을 λ°°μ—΄μ•ˆμ— λ„£μ—ˆλ‹€. 

[1, 1, 2, ...] 

λ§Œμ•½ λ‹€μ‹œ f(3) 값이 ν•„μš”ν•  경우 memoμ—μ„œ n번째 인덱슀 값을 λ¦¬ν„΄ν•˜λ©΄ λœλ‹€. 

 

이 κ²½μš°μ—λŠ” μ‹œκ°„λ³΅μž‘λ„κ°€ μ΅œλŒ€ O(n)이 λœλ‹€.

 

 

 

 

 

3. Bottom-up Approach

function fib(n){
  if (n === 1 || n === 2){
    return 1;
  } else {
  let array = [];
  array.push(1,1);
    for(let i = 2; i < n; i++){
      array.push(array[i-2] + array[i-1]);
    }
  }
  return array[array.length - 1];
}

 

fib(1) λΆ€ν„° fib(n)κΉŒμ§€μ˜ 값을 array에 μ €μž₯μ‹œν‚€λŠ” 방식이닀. fib(1) λΆ€ν„° fib(2)κΉŒμ§€ μ±„μ›Œλ„£κ³ ([1,1]), fib(3) λΆ€ν„°λŠ” fib(3-1) + fib(3-2) 둜 값을 nκΉŒμ§€ κ΅¬ν•˜λŠ” 방식이닀. 

 

이 경우 μ‹œκ°„λ³΅μž‘λ„λŠ” O(n)이닀.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[참고자료]