[알고리즘] 재귀 호출, 재귀 함수란?

2021. 2. 8. 01:36·알고리즘
728x90

컴퓨터가 수행하는 많은 작업들은 대개 작은 조각들로 나누어 볼 수 있다.

예를 들어, 문자열의 길이를 세는 작업은 첫 한 글자를 세기, 다음 한 글자 세기, 다음 한글자를 세기 ... 라는 조각들로 나눌 수 있을 것이다.

이런 작업을 구현할 때 유용하게 사용되는 개념이다.

 

재귀 함수 (recursive function) or 재귀 호출 (recursion)

자신이 수행할 작업을 유사한 형태의 여러 조각으로 쪼갠 뒤 그 중 한 조각을 수행하고, 나머지를 자기 자신을 호출해 실행하는 함수

 

코드

1부터 n까지의 합을 계산하는 반복 함수(sum)과 재귀 함수(recursiveSum)

 

// 필수 조건 : n >= 1
// 결과 : 1부터 n까지의 합을 반환한다.
int sum(int n) {
	int ret = 0;
	for (int i = 1; i <= n; ++i)
		ret += i;
	return ret;
}

// 필수 조건 : n >= 1
// 결과 : 1부터 n까지의 합을 반환한다.
int recursiveSum(int n) {
	if (n == 1) return 1; // 더이상 쪼개지지 않을 때
	return n + recursiveSum(n - 1);
}

 

재귀 호출을 이용하기 위해서,

n개의 숫자의 합을 구하는 작업을 n개의 조각으로 쪼개, 더할 각 숫자가 하나의 조각이 되도록 하자.

 

이 조각 중에 n만 따로 빼서 본다면, 나머지는 1부터 n-1까지의 조각들이 남는다.

 

나머지는 결국 1부터 n-1까지의 합이다. 따라서 자기 자신을 호출하여 n-1까지의 합을 구하고, 여기에 n을 더하면 우리는 원하는 답을 얻을 수 있다.

 

n이 1인 경우 조각이 하나 뿐이기 때문에, 곧바로 1을 반환하는 조건문을 추가한다.

 

기저 사례 (base case)

재귀 함수는 더이상 쪼개지지 않는 최소한의 작업에 도달했을 때 답을 곧바로 반환하는 조건문을 포함해야 한다. 이때 쪼개지지 않는 가장 작은 작업들을 뜻한다.

 

기저 사례를 선택할 때는, 존재하는 모든 입력이 항상 기저 사례의 답을 이용해 계산될 수 있도록 해야 한다.

 

ex) recursiveSum()에서 n이 2인지 확인하여 3을 반환하는 경우, 입력이 1이 들어오면 문제가 생기게 된다.

따라서 기저 사례는 n==1인 경우에 1을 반환하는 것이 옳다.

 

 

예제 : 보글 게임 (문제 ID : BOGGLE, 난이도 : 하)

보글(Boggle)은 그림과 같은 5*5 크기의 알파벳 격자를 가지고 하는 게임이다. 이때 게임의 목적은 상하좌우/대각선으로 인접한 칸들의 글자들을 이어서 단서를 찾아내는 것이다.

예를 들어, [ PRETTY, GIRL, REPEAT ] 와 같은 단어들을 (b), (c), (d)와 같이 찾을 수 있다.

각 글자들은 대각선으로도 이어질 수 있으며, 한 글자가 두 번 이상 사용될 수도 있다. 주어진 칸에서 시작해서 특정 단어를 찾을 수 있는지 확인하는 문제를 풀어보자.

hasWord(y, x, word) = 보글 게임판의 (y, x)에서 시작하는 단어 word의 존재 여부를 반환한다.

 

고민하기

이 문제를 풀 때 까다로운 점은 다른 글자가 될 수 있는 칸이 여러 개 있을 때, 이 중 어느 글자를 선택해야 할지 미리 알 수 없다는 것이다.

만약 (e) 그림의 가운데 칸에서 시작하는 단어 "YES"를 찾고 싶다고 하자.

"Y" 주변의 8칸에 모두 "E"가 들어 있지만 이중 "YES" 로 연결되는 "E"는 하나 뿐이며, "E"만 살펴본다면 어느 "E"를 따라가야 답을 찾을 수 있는지 알 수 없다.

-> 완전 탐색을 이용해, 단어를 찾아낼 때 까지 모든 인접한 칸을 하나씩 확인하는 것이 어떨까?

 

풀이

hasWord()가 하는 일은 조각내는 방법은 각 글자를 하나의 조각으로 만드는 것이다.

함수를 호출할 때 단어의 시작 위치를 정해주기 때문에, 문제의 조각 중 첫 번째 글자에 해당하는 조각을 간단히 해결할 수 있다.

- 시작 위치에 쓰여 있는 글자가 단어의 첫 글자와 다르다면 바로 false를 반환하고 종료하면 된다.

- 그리고 나서 원래 단어 word에서 첫 글자를 뗀 단어 word[1..]을 격자에서 찾아야 한다.

- 이를 반복하면 된다. 간단하다.

 

기저 사례의 선택

더 이상 탐색 없이 간단히 답을 낼 수 있는 다음 경우들을 기저 사례로 선택한다.

1. 위치 (y, x)에 있는 글자가 원하는 단어의 첫 글자가 아닌 경우 항상 실패

2. (1번 경우에 해당되지 않을 경우) 원하는 단어가 한 글자인 경우 항상 성공

 

구현

보글 게임판에서 단어를 찾는 재귀 호출 알고리즘

 

const int dx[8] = { -1, -1, -1,  1,  1,  1,  0,  0 };
const int dx[8] = { -1,  0,  1, -1,  0,  1, -1,  1 };
// 5*5의 보글 게임 판의 해당 위치에서 주어진 단어가 시작하는지를 반환
bool hasWord(int y, int x, const string& word) {
	// 기저 사례 1 : 시작 위치가 범위 밖이면 무조건 실패
	if (!inRange(y, x)) return false;
	// 기저 사례 2 : 첫 글자가 일치하지 않으면 실패
	if (board[y][x] != word[0]) return false;
	// 기저 사례 3 : 단어 길이가 1이면 성공
	if (word.size() == 1) return true;
	// 인접한 여덟 칸을 검사한다.
	for (int direction = 0; direction < 8; ++direction) {
		int nextY = y + dy[direction], nextX = x + dx[direction];
		// 다음 칸이 범위 안에 있는지, 첫 글자는 일치하는지 확인할 필요가 없다.
		if (hasWord(nextY, nextX, word.substr(1)))
			return true;
	}
	return false;
}

 

함수안에서 하나의 문자만 확인하고, 나머지는 다시 hasWord(자기자신)을 호출하여 나머지를 확인한다.

+ 상대좌표를 별도의 변수로 분리해 낸 것도 참고하자.

 

시간 복잡도 분석

완전 탐색 알고리즘의 시간 복잡도를 계산하는 것은 단순하다. 완전 탐색은 가능한 답 후보들을 모두 만들어 보기 때문에, 시간 복잡도를 계산하기 위해서는 가능한 후보의 수를 전부 세어 보면 된다.

-> O(8^N)

728x90

'알고리즘' 카테고리의 다른 글

[알고리즘] 버블 정렬 (Bubble Sort) 이란?  (0) 2022.06.24
[알고리즘] '재귀 호출'과 '완전 탐색'으로 문제를 해결하는 방법, 재귀 호출 문제  (0) 2021.02.10
알고리즘의 시간 복잡도 분석 - 5. 시간 복잡도  (0) 2021.01.20
알고리즘의 시간 복잡도 분석 - 4.지수 시간 알고리즘  (0) 2021.01.13
알고리즘의 시간 복잡도 분석 - 3.선형 이하 시간 알고리즘  (0) 2021.01.13
'알고리즘' 카테고리의 다른 글
  • [알고리즘] 버블 정렬 (Bubble Sort) 이란?
  • [알고리즘] '재귀 호출'과 '완전 탐색'으로 문제를 해결하는 방법, 재귀 호출 문제
  • 알고리즘의 시간 복잡도 분석 - 5. 시간 복잡도
  • 알고리즘의 시간 복잡도 분석 - 4.지수 시간 알고리즘
kiminae
kiminae
공부한 내용을 정리합니다.
  • kiminae
    데이터 다루는 사람
    kiminae
  • 전체
    오늘
    어제
    • 분류 전체보기 (67)
      • AI & 빅데이터 (6)
        • kafka (10)
        • [Book] 빅데이터를 지탱하는 기술 (12)
      • 알고리즘 (19)
      • 알고리즘 문제풀이 (13)
        • programmers (0)
        • 백준 (1)
        • LeetCode (12)
      • Android (3)
      • Book&Lesson (13)
        • [Lesson] 프로그래머스 커뮤러닝 (Pyth.. (1)
      • 참고한 글들 (1)
      • 컨퍼런스 정리 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    버블정렬
    정렬
    머신러닝
    트리
    MPP데이터베이스
    릿코드
    데이터시각화
    leetcode
    sort
    mvvm
    알고리즘
    빅데이터를지탱하는기술
    파이프라인구축
    Algorithm
    데이터엔지니어
    리트코드
    정렬알고리즘
    DP문제
    카프카
    알고리즘문제
    빅데이터
    ViewModel
    알고리즘풀이
    추천알고리즘
    개인화추천
    hadoop
    카프카클라이언트
    BI도구
    Kafka
    시간복잡도
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
kiminae
[알고리즘] 재귀 호출, 재귀 함수란?
상단으로

티스토리툴바