[알고리즘] 순열 알고리즘

순열 알고리즘

순열이란?

서로 다른 n개 중 r개를 골라 순서를 고려해 나열한 경우의 수

ex) 1, 2, 3, 4 의 배열에서 두 개의 원소를 고를 때 가능한 경우의 수는

1 2 / 1 3 / 1 4/ 2 3 / 2 4 / 3 4 총 6가지가 나오게 된다.


swap을 이용한 순열 알고리즘

image

(이미지 출처)

swap 함수를 만들어 배열의 값을 직접 바꾸는 방법

배열의 첫 번째 값부터 하나씩 바꾸며 모든 값을 한 번씩 swap하는 방법이다.

permutation 호출 이후에 swap 메소드를 한 번 더 호출하여 배열의 순서를 보장해준다.

static void permutation(int[] arr, int depth, int n, int r) {
    if(depth == r) {
        print(arr, r);
        return;
    }

    for(int i = depth; i < n; i++) {
        swap(arr, depth, i);
        permutation(arr, depth + 1, n, r);
        swap(arr, depth, i);
    }
}

static void swap(int[] arr, int depth, int i) {
    int temp = arr[depth];
    arr[depth] = arr[i];
    arr[i] = temp;
    
    // 아래는 swap 결과 보기 위해 임의로 추가한 코드
    System.out.print("switch: ");
    for (int num : arr) {
        System.out.print(num);
    }
    System.out.println();
}

static void print(int[] arr, int r) {
    for(int i = 0; i < r; i++)
        System.out.print(arr[i] + " ");
    System.out.println();
}


배열 {1, 2, 3} 이 들어왔을 때 예시

① 한 가지를 고를 때 (n = 3, r = 1)

swap(arr, 0, 0) => 실행 결과 {1, 2, 3} => permutation(arr, 1, 3, 1) => 1 출력 => 배열 {1, 2, 3} 으로 복귀

swap(arr, 0, 1) => 실행 결과 {2, 1, 3} => permutation(arr, 1, 3, 1) => 2 출력 => 배열 {1, 2, 3} 으로 복귀

swap(arr, 0, 2) => 실행 결과 {3, 2, 1} => permutation(arr, 1, 3, 1) => 3 출력 => 배열 {1, 2, 3} 으로 복귀

image


② 두 가지를 고를 때 (n = 3, r = 2)

swap(arr, 0, 0) => 실행 결과 {1, 2, 3} => permutation(arr, 1, 3, 1) => swap(arr, 1, 1) => 실행 결과 {1, 2, 3} => permutation(arr, 2, 3, 1) => 1 2 출력 => 배열 {1, 2, 3} 으로 복귀 => swap(arr, 1, 2) => 실행 결과 {1, 3, 2} => permutation(arr, 2, 3, 2) => 1 3 출력 => 배열 {1, 2, 3} 으로 복귀

swap(arr, 0, 1) => 실행 결과 {2, 1, 3} => permutation(arr, 1, 3, 1) => swap(arr, 1, 1) => 실행 결과 {2, 1, 3} => permutation(arr, 2, 3, 1) => 2 1 출력 => 배열 {2, 1, 3} 으로 복귀 => swap(arr, 1, 2) => 실행결과 {2, 3, 1} => permutation(arr, 2, 3, 2) => 2 3 출력=> swap(arr, 1, 2) => 배열 {2, 1, 3} 으로 복귀

=> swap(arr, 0, 1) => 초기 배열 {1, 2, 3} 으로 완전 복귀

image


r = 1 일 때 까지는 이해하기 쉬웠지만, r = 2 부터는 재귀로 깊숙히 들어가기 때문에

직접 디버깅을 돌려 이해해 보는 것이 필요하다.