순열, 조합, 부분집합 알고리즘

nunddu·2021년 3월 24일

완전탐색 문제를 풀기위해 필수적으로 알아야되는 순열, 조합, 부분집합, 다음순열의 개념과 간략화한 알고리즘을 정리하려고 한다.

0. 공통 변수와 의미

순열, 조합, 부분집합, 다음순열에서 공통적으로 사용할 변수와 의미는 다음과 같다.

int N : 선택의 후보가 될 수 있는 N개의 수
int M : 선택하고자 하는 원소의 수
int cnt : 재귀 호출 내에서 해당 시점에서 선택한 원소의 수를 의미
boolean[] isSelected : 재귀 수행 중 과거에 해당 원소를 이미 선택을 했는지 판단하기 위한 boolean 배열
int[] selectedArr : 선택한 원소를 저장할 int 배열

1. 순열

순열은 주어진 숫자들의 순서를 고려하여 조합을 만들기 때문에 아래 두 가지를 체크해야한다.

  • 후보가 되는 수들을 매번 처음부터 확인
  • 해당 수가 이미 선택된 수인지 확인
// 1 ~ N 사이의 값 중에 M개 뽑기
 public void permutation(int cnt) {
 	if (cnt == M) {
    	    // 순열을 완성했을 때 처리할 로직
            return;
  	}
    	for (int i = 0; i < N; i++) {
    	    if (isSelected[i]) continue;
            selectedArr[cnt] = i + 1;
            isSelected[i] = true;
            permutation(cnt + 1);
            isSelected[i] = false;
        }
 }
            

2. 조합

조합은 주어진 숫자들의 순서를 고려하지 않고 조합을 만들기 때문에 순열처럼 선택 여부를 확인할 필요가 없다. 조합을 함수를 호출 할 때 현 시점에서 선택한 인덱스 + 1의 값을 매개변수로 넘겨주어 항상 다음 수 부터 선택하도록 구현할 수 있다.

  • 다음으로 선택할 index값을 함수의 파라미터로 전달한다.
  • 함수 수행 시점에서 선택할 값은 index부터 시작한다.
// 1 ~ N 사이의 값 중에 M개 뽑기
 public void combination(int cnt, int index) {
 	if (cnt == M) {
    	    // 조합을 완성했을 때 처리할 로직
            return;
  	}
    	for (int i = index; i < N; i++) {
    	    selectedArr[cnt] = i + 1;
            combination(cnt + 1, i + 1);
        }
 }
            

3. 부분집합

집합의 원소들을 선택, 비선택하는 과정을 통해 공집합을 포함한 각기 다른 부분집합을 구할 수 있다.

  • 매 시점에서 원소의 선택과 비선택을 마킹하고 재귀함수를 호출한다.
  • 원소를 M개 선택한 시점에서 선택 마킹 정보를 이용해 부분집합을 완성한다.
// 1 ~ N 사이의 값 중에 M개 뽑기
 public void subset(int cnt) {
 	if (cnt == M) {
    	    for (int i = 0; i < M; i++) {
            	if (isSelected[i]) {
                   // 완성된 부분집합에 포함된 요소이면 
                }
            }
    	    // 조합을 완성했을 때 처리할 로직
            return;
  	}
    	isSelected[cnt] = true; // 선택
       	subset(cnt + 1);
        isSelected[cnt] = false; // 비선택
       	subset(cnt + 1);
 }
            

4. 다음순열

작성중

0개의 댓글