[Algorithm] 재귀적 함수(순열,조합,하노이탑)

개발개발·2021년 7월 5일
0

재귀 함수는 자신을 다시 호출하는 함수다. 반복문과 같이 일정한 조건을 충족할때까지 계속 자기 자신을 반복하는 함수다.

대표적으로 피보나치 수열이 있다.
ex) 1, 1, 2, 3, 5, 8, ...
a(n) = a(n-1) + a(n-2)를 계산하는 것이다.

이때 n값이 주어졌을 때 결과값을 return하는 함수를 만들어 보겠다.

public int fibo(int n){
       if(n == 1 || n == 2){
           return 1;
       }
       return fibo(n-1) + fibo(n-2);
   }

수열을 만드는 식을 그대로 활용하면 피보나치 수열에 대한 재귀함수를 만들 수 있다. 위 함수의 단점으로는 효율성이 떨어진다. 한번 계산한 내용도 반복해서 계산하기 때문이다. 이를 해결하기 위해서는 동적계산법으로 해결할 수 있다. 한번 계산한 내용을 저장해두고 다시 계산해야되는 경우 결과값만 꺼내서 사용하는 것인데 추후 다루도록 하겠다.

외부에 static으로 변수를 추가해도 원하는 계산을 할때 유용하다.

이 글을 쓰면서 본격적으로 다루고 싶었던 내용은 순열과 조합이다. 고등학생때 nCr, nPr 하면서 원리와 공식을 알고 사용하던 것들이라 우습게 생각했다. 하지만 어떤 조합이 나오는지 하나씩 만들어서 뽑아내는 걸 만드려니 어려웠는데 재귀로 해결하는 글을 읽었고 공부하게 되었다.

// 순열
public void permutation(List<String> arr, List<String> result, int n, int r) {
       if (r == 0) {
           System.out.println(result.toString());
           return;
       }

       for (int i = 0; i < n; i++) {
           result.add(arr.remove(i));
           perm2(arr, result, n - 1, r - 1);
           arr.add(i, result.remove(result.size() - 1));
       }
   }

재귀 함수를 반복문안에서 쓰게 되니 디버깅하면서 상당히 헷갈렸다. 이곳저곳 통통 튀어다니는 느낌이라 몇번째 재귀인지 파악하면서 이해하려고 노력했다.

// 조합
public void combination(List<String> arr, List<String> result, int index, int n, int r) {

       if (r == 0) {
           System.out.println(result.toString());
           return;
       }

       for (int i = index; i < n; i++) {

           result.add(arr.get(i));
           combination(arr, result, i + 1, n, r - 1);
           result.remove(result.size() - 1);
       }
   }

마지막으로 재귀함수에 대해서 조금 더 이해할 겸 하노이의 탑 문제를 공부해봤다.

하노이의 탑 문제는 3개의 봉을 이용해서 n개의 원판을 이동하는 것이다. 이때 몇가지 규칙이 있는데 규칙은 아래와 같다.
1. 한번에 원판 한개만 이동할 수 있다.
2. 원판마다 크기가 다르다.
3. 모든 원판은 크기가 큰 순서대로 밑에서부터 쌓여있다.
4. 큰 원판 아래 작은 원판이 올수 없다.
5. 모든 원판을 같은 순서대로 다른 봉으로 옮겨야 한다.

-> 이때 옮겨야 되는 횟수와 원판이 어디서 어디로 옮겨가는지 파악하는 문제다.

이 문제의 점화식은 a(n) = 2 * a(n-1) + 1이다. 조금 상세하게 설명하면 a(n)은 a(n-1)개를 다른 봉으로 이동하면 1개의 원판이 남는다. 이 원판을 목적지로 이동하고 a(n-1)개를 다시 목적지로 이동하면 a(n)이 완성된다.

이 내용을 재귀함수로 만들면 아래와 같다. 이때 횟수는 count를 이용해서 파악할 수 있다.

// n개의 원판을 start, mid, to 3개의 봉을 이용해서 옮긴다.
public static int count = 0;
public static void Hanoi(int n, int start, int mid, int to) {
	count++;
       // 가장 위에있는 원판만 움직일 수 있다
       if (n == 1) {
           System.out.println(start + " " + to);
           return;
       }

       // #1 n-1개를 중간으로 이동
       Hanoi(n - 1, start, to, mid);

       // #2 : 가장 아래있는 원판을 목적지로 이동
       System.out.println(start + " " + to);

       // #3 중간에 이동된 n-1개의 원판을 목적지로 이동
       Hanoi(n - 1, mid, start, to);

   }

재귀함수에 대해서 활용하면서 조금더 자세하게 알아봐야 겠다. 깊이우선탐색에서도 쓰이니 다른 알고리즘을 공부하면서 더욱 확고하게 알아둬야겠다.

reference
-https://codevang.tistory.com/297 (순열과 조합)
-https://st-lab.tistory.com/96 (하노이 탑)

profile
청포도루이보스민트티

0개의 댓글