[Kotlin] 알고리즘 - 순열(Permutation)

Kotlin

(Update : 2023-10-12)

Language :

순열(Permutation)

서로 다른 n개를 가진 원소에서 r개를 중복없이 골라 순서대로 나열한다.

조합에서 확장된 개념으로, 조합과 달리 순서가 있다.

방문 여부 배열을 이용한 처리 과정

  1. 깊이 우선 탐색(DFS, Depth-First Search)으로 모든 원소를 방문한다.
  2. 방문 여부를 확인하여 값이 중복되지 않게 한다.
  3. 깊이가 r과 일치할 때까지 위 과정을 반복한다.

코드 1. n!

kotlin

private const val alphabets = "ABC"
private lateinit var result: Array<Char> // 정렬된 알파벳
private lateinit var visited: Array<Boolean> // 순열 방문 여부

fun main() {
    result = Array(alphabets.length) { '_' }
    visited = Array(alphabets.length) { false }

    permutation()
}

fun permutation(depth: Int = 0) {
    if (depth == alphabets.length) {
        println(result.contentDeepToString()) // 완성된 순열
        return
    }
    
    for (i in alphabets.indices) {
        if (visited[i]) continue
        
        result[depth] = alphabets[i]
        visited[i] = true
        permutation(depth + 1)
        visited[i] = false
    }
}

코드 2. nPr + 제네릭 타입으로 모든 데이터 타입 대응

kotlin

fun main() {
    permutation(arrayOf("A", "B", "C"), 3).forEach {
        println(it)
    }
}

/**
 * 순열
 *
 * @param elements: n개 원소를 가진 배열
 * @param r: 추출할 원소 수
 */
fun <T> permutation(elements: Array<T>, r: Int): List<List<T>> {
    val visited = BooleanArray(elements.size) // 방문 여부
    val results = mutableListOf<List<T>>() // 모든 경우의 수

    val result = elements.sliceArray(0 until r) // r개를 정렬한 결과

    fun recursionPermutation(depth: Int = 0) {
        if (depth == r) {
            results.add(result.toList())
            return
        }

        for (i in elements.indices) {
            if (visited[i]) continue
            
            visited[i] = true
            result[depth] = elements[i]
            recursionPermutation(depth + 1)
            visited[i] = false
        }
    }

    recursionPermutation()
    return results
}

코드 3. nPr + swap을 이용한 처리

kotlin

fun main() {
    permutation(arrayOf("A", "B", "C"), 3).forEach {
        println(it)
    }
}

fun <T> permutation(elements: Array<T>, r: Int): List<List<T>> {
    val n = elements.size
    val results = mutableListOf<List<T>>() // 모든 경우의 수

    fun swapPermutation(depth: Int = 0) {
        if (depth == r) {
            results.add(elements.takeLast(r).toList())
            return
        }

        for (i in range(depth, n)) {
            var tmp = elements[i]
            elements[i] = elements[depth]
            elements[depth] = tmp

            swapPermutation(depth + 1)

            // 원위치
            tmp = elements[i]
            elements[i] = elements[depth]
            elements[depth] = tmp
        }
    }

    swapPermutation()
    return results
}

결과

text

[A, B, C]
[A, C, B]
[B, A, C]
[B, C, A]
[C, A, B]
[C, B, A]

Reference

https://velog.io/@chayezo/Algorithm-순열-조합

https://hanyeop.tistory.com/455

https://notepad96.tistory.com/111

https://programming4myself.tistory.com/94

https://velog.io/@soyeon207/알고리즘-순열-yaxs0hh7

https://gyeong-log.tistory.com/51

https://soopeach.tistory.com/157

https://velog.io/@heyday_7/알고리즘-순열과-조합

민갤

Back-End Developer

백엔드 개발자입니다.