재귀

Updated:

재귀

반복과 재귀

  • 반복과 재귀는 유사한 작업을 수행할 수 있다.
  • 반복은 수행하는 작업이 완료될 때까지 계속 반복
    • 루프(for/while, do-while구조)
  • 재귀는 주어진 문제의 해를 구하기 위해 동일하면서 더 작은 문제의 해를 이용하는 방법
    • 하나의 큰 문제를 해결할 수 있는(해결하기 쉬운) 더 작은 문제로 쪼개고 결과들을 결합한다.
    • 재귀 함수로 구현

재귀함수

  • 함수 내부에서 직접 혹은 간접적으로 자기 자신을 호출하는 함수
  • 일반적으로 재귀적 정의를 이용해서 재귀함수를 구현한다.
  • 따라서, 기본부분(basic part)와 재귀의 끝과 유도 파트(inductive part)로 구성된다.
  • 재귀적 프로그램을 작성하는 것은 반복 구조에 비해 간결하고 이해하기 쉽다. 그러나, 재귀에 대해 익숙하지 않은 개발자들은 재귀적 프로그램이 어렵다고 느낀다
  • 함수 호출은 프로그램 메모리 구조에서 스택을 사용한다.
  • 따라서, 재귀 호출은 반복적인 스택의 사용을 의미하며, 메모리 및 속도에서 성능저하가 발생한다

팩토리얼 재귀함수

  • 재귀적 정의
    • Basic rule : N<=1인 경우, N=1
    • Inductive rule : N>1, n!= n * (n-1)
  • n!에 대한재귀함수
     int factorial(int n//구해지는 값이 결정적 요인 바뀌는 값, 매개변수 설계){
     	if(n<=1)
         return 1;
     else
         return n*factorial(n-1);
     }
    

피보나치 재귀함수

  • 이전의 두 수 합을 다음 항으로 하는 수열을 피보나치라 한다.
  • 피보나치 수열의 i번째 값을 계산하는 함수F를 정의하면 다음과 같다.
  • F0 = 0; F1 = 1
  • Fi = Fi-1 + Fi-2 for i>=2
  • 위의 정의로부터 피보나치 수열의 i번째 항을 반환하는 함수를 재귀함수로 구현할 수 있따.
  • 피보나치 수를 구하는 재귀함수
    fibo(n)
     if(n <= 1) then
         return n;
     else
         return fibo(n-1) + fibo(n-2);
    end fibo(n)
    
  • 단, 위의 예에서 피보나치 수를 구하는 함수를 재귀함수로 구현한 알고리즘은 문제가 있다.
  • “엄청난 중복 호출이 존재한다”는 것이다.

Memoization

  • 앞의 예에서 피보나치 수를 구하는 알고리즘에서 fibo(n)의 값을 계산하자마자 저장하면(memoize), 실행시간을 O(n)으로 줄일 수 있다.

//memo를 위한 배열을 할당하고, 모두 0으로 초기화 한다.
//memo[0]을 0으로 memo[1]은 1로 초기화 한다.

fibo(n)
	if n>=2 and memo[n] is zero then
		memo[n] <- fibo(n-1) + fibo(n-2);
	return memo[n];
end fibo()  

반복 또는 재귀?

  • 해결할 문제를 고려해서 반복이나 재귀의 방법을 선택
  • 재귀는 문제 해결을 위한 알고리즘 설계가 간단하고 자연스럽다
  • 추상자료형(List, tree)등의 알고리즘은 재귀적 구현이 간단하고 자연스러운 경우가 많다.
  • 일반적으로, 재귀적 알고리즘은 반복알고리즘보다 더 많은 메모리와 연산을 필요로 한다.
  • 입력 값 n이 커질수록 재귀 알고리즘은 반복에 비해 배효율적일 수 있다.
  재귀 반복
종료 재귀함수 호출이 종료되는 베이스케이스(base case) 반복문의 종료조건
수행시간 (상대적)느림 빠름
메모리공간 (상대적)많이 사용 적게사용
소스코드길 짧고 간결 길다
소스 코드 형태 선택 구조(if…else) 반복 구조(for, while)
무한 반복시 스택 오버플로우 cpu를 반복해서 점유

하노이의 탑 문제

  • 제약사항 * 원판 번호는 맨 위부터 1,2,3 순 부여
    • 기둥은 3개로 가정한다(기둥 번호는 왼쪽부터 1,2,3순으로 부여)
    • 시작은 N개의 원판이 1번 기둥에 쌓여져 있다
  • 입력 * 원판 개수인 N을 입력 받는다.(1<=N<=10)
    • 원판 번호는 맨 위부터 1,2,3 순으로 부쳐
  • 출력 * 옮겨지는 원판 번호 : 출발 기둥 번호 -> 목적 기둥 번호
public class Main{
	private static void hanoi(int n, int start, int temp, int dest){
		if(n==0) return;
		//자신의 위쪽의 n-1개의 원판 들어내기 : 임시기둥으로 옮기기
		hanoi(n-1,start,dest,temp);
		//자신의 원판 옮기기 : 목적기둥
		System.out.pritnln(n + ":" + start + ">" + dest);
		//들어냈던 임시기둥의 n-1개 원판 자신위에 쌓기 : 목적기둥으로 옮기기
		hanoi(n-1,temp,start,dest);
	}
	public static void main(String[] args{
		hanoi(3,1,2,3);
	}
}

Leave a comment