인프런 커뮤니티 질문&답변

박승훈님의 프로필 이미지
박승훈

작성한 질문수

10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트

[필수개념] 순열 : 재귀함수로 만드는 순열

순열을 재귀함수로 구현했을 때의 장점

해결된 질문

작성

·

1.1K

5

안녕하세요, 선생님

 

다름이 아니라 순열을 재귀 함수로 구현했을 때의 장점이 많을 것 같아 질문 드립니다.

 

선생님께서 영상 중간에 재귀 함수로 순열을 구현하는 것보다 next_permutation을 사용하여 순열을 구현하는 것이 더 좋다고 말씀하셨습니다. 저 또한 선생님께서 말씀하신 부분을 충분히 동의하지만, 한 가지 의문이 드는 부분이 있습니다.

 

next_permutation을 통해 순열을 구현하고자 할 때, 순열을 적용하고자 하는 컨테이너가 정렬되어 있어야 한다는 전제 조건이 필요합니다. 따라서 우리가 어떤 알고리듬 문제를 next_permutation으로 해결하고자 할 때, 평균적으로 O(nlogn)의 성능이 필요합니다. 반면, 재귀 함수로 순열을 구현하는 경우 순열을 적용하고자 하는 컨테이너의 순서와 상관없이 순열을 바로 적용할 수 있습니다. 그렇다면, 재귀 함수를 통해 순열을 구현하는 방법을 선호하는 것이 더 좋지 않을까요? 물론, 재귀 함수를 처리하는 데 드는 비용이 정렬을 처리하는 데 드는 비용보다 높다면 next_permutation 함수를 사용하는 것이 더 효율적이라고 생각합니다!

 

항상 좋은 강의 해주셔서 감사합니다 ㅎㅎ!

답변 1

6

큰돌님의 프로필 이미지
큰돌
지식공유자

안녕하세요 승훈님 ㅎㅎ

next_permutation을 통해 순열을 구현하고자 할 때, 순열을 적용하고자 하는 컨테이너가 정렬되어 있어야 한다는 전제 조건이 필요합니다. 따라서 우리가 어떤 알고리듬 문제를 next_permutation으로 해결하고자 할 때, 평균적으로 O(nlogn)의 성능이 필요합니다. 반면, 재귀 함수로 순열을 구현하는 경우 순열을 적용하고자 하는 컨테이너의 순서와 상관없이 순열을 바로 적용할 수 있습니다. 그렇다면, 재귀 함수를 통해 순열을 구현하는 방법을 선호하는 것이 더 좋지 않을까요? 물론, 재귀 함수를 처리하는 데 드는 비용이 정렬을 처리하는 데 드는 비용보다 높다면 next_permutation 함수를 사용하는 것이 더 효율적이라고 생각합니다!

>>

네 sort 는 O(nlogn)이 무조건 필요하죠. 날카로운 질문이네요. ㅎㅎ

( 이런 비판적으로 생각하는 자세는 정말 좋습니다.ㅎㅎ)

결론부터 말씀드리면, 성능으로 따졌을 때 재귀함수가 더 좋지만, 코테에서는 무관하며, 코테에서는 next_permutation을 쓰는게 보통은 더 좋습니다.

 

왜 그럴까요?

자, nP2이상인 경우 보통 O(n^2) 이상의 시간복잡도가 필요합니다.

이런 공식을 가지니까요. ㅎㅎ

image즉, 문제에서 순열을 내는 문제는 보통은 O(n^2) 이상의 시간복잡도는 허용하는 문제임이 자명합니다.

 

그렇기 때문에 O(n^2) 보다 낮은 수준의 O(nlogn)의 시간복잡도가 추가된다고 한들 문제를 푸는데 미치는 영향은 거의 없다고 보시면 됩니다.

또한, next_permutation이란 함수는 내부에서는 lexicographical_compare 를 기반으로 순열을 만듭니다. 해당 부분은 간단한 반복문으로 이루어져 있구요.

template <class InputIterator1, class InputIterator2>
  bool lexicographical_compare (InputIterator1 first1, InputIterator1 last1,
                                InputIterator2 first2, InputIterator2 last2)
{
  while (first1!=last1)
  {
    if (first2==last2 || *first2<*first1) return false;
    else if (*first1<*first2) return true;
    ++first1; ++first2;
  }
  return (first2!=last2);
}

 

따라서 보통 코테를 풀 때 순열이 필요한 경우에 next_permutation으로 하는게 좋습니다.

 

하지만!!

이거는 코테에서 그렇다는 것이구 성능상으로 봤을 때는 sort + next 보다는 재귀함수로 구축하는게 더 빠릅니다. 보통은 재귀함수를 호출하는데 드는 코스트가 더 크지만, next_permutation은 몇개중에 몇개를 뽑는다기 보다는 전체 배열을 계속해서 섞어서 순열을 무조건 만들어야 하기 때문입니다.

그렇다면 두개를 정확히 비교해볼까요?

sort + next_permutation과 재귀함수를 비교하는 벤치마킹을 시도해봤습니다. 원래는 n을 100만 이상으로 하는데 permutation 자체가 팩토리얼이기 때문에 n은 7 * 2 = 14 로 했습니다. (그 이상은 벤치마킹 자체가 안됩니다)

https://perfbench.com/

// ------------------------------------------------------------------------- //
// Welcome to Perfbench: online C++ code profiling tool.                     //
// Below is a short boilerplate to start with if you are familiar with API.  //
// Otherwise, use the main.cpp example or read the "help" and "api" tabs.    // 
// ------------------------------------------------------------------------- //

#include <bits/stdc++.h>
using namespace std; 
void makePermutation(int n, int r, int depth, vector<int> & v){
    if(r == depth){  
        return;
    }
    for(int i = depth; i < n; i++){
        swap(v[i], v[depth]);
        makePermutation(n, r, depth + 1, v);
        swap(v[i], v[depth]);
    }
    return; 
} 

void A(){
  vector<int> v;
  int n = 7;
  for(int i = 1; i < n; i++){
    v.push_back(i);
    v.push_back(n - i);
  }
  sort(v.begin(), v.end()); 
  while(next_permutation(v.begin(), v.end()));
}
 
void B(){
  vector<int> v;
  int n = 7;
  for(int i = 1; i < n; i++){
    v.push_back(i);
    v.push_back(n - i);
  }    
  makePermutation(n, n, 0, v);
}
void test_latency(size_t iteration) {  

  PROFILE_START("A"); 
  A();
  PROFILE_STOP("A");
  PROFILE_START("B");
  B();
  PROFILE_STOP("B");
}

int main() {
  const size_t warmups = 100;
  const size_t tests = 100;
 
  PROFILE_RUN_ALL(warmups, tests, 
    test_latency(__loop);
  );

  return 0;
}

image

앞의 그림처럼 B가 더 낮은 것을 볼 수 있습니다.

즉, 재귀함수가 더 빠릅니다. 그러나 코테에서 순열이 나온다는것은 어차피 O(n^2)이상의 시간을 허용하는 것이라 이와는 상관없이 next_permutation으로 구축해도 무방합니다.

 

 

또 질문 있으시면 언제든지 질문 부탁드립니다.

좋은 수강평과 별점 5점은 제가 큰 힘이 됩니다. :)

감사합니다.

강사 큰돌 올림.

박승훈님의 프로필 이미지
박승훈

작성한 질문수

질문하기