M$ C++ 컴파일러 2003에서 나름대로 random_sample_n 쓰기

danskesb의 이미지

쌍둥이: http://ubuntu.ksa.hs.kr/~psj/blog/node/75

추가: 이 함수는 SGI의 확장 구현입니다.
Microsoft C++ Compiler 2003의 algorithm 헤더에는 <a href="http://www.cppreference.com/cppalgorithm/random_sample_n.html">random_sample_n</a> 함수가 없다는 것을 알게 되었다. 이유인 즉슨, 현재 작성중인 QOM의 일부만 돌기 기능을 구현하다가 몇개만 랜덤으로 추출하는 STL에 대해서 동작하는 함수가 필요했는데, 구글신은 이 함수를 권해 주었다. 그래서, 쓰고 있는 VS.NET 2003에 이 코드를 집어넣고 돌렸다.

std::vector<Wordlet> tmpvec;
random_sample_n(tmpset.wordsets.begin(), tmpset.wordsets.end(), tmpvec.begin(), word_count);
tmpset.wordsets = tmpvec;

보시다시피 vector 클래스를 분명히 존재하는 함수로 컴파일해 가고 있었다. 그런데 결과는...
------ 빌드 시작: 프로젝트: libQOpenMemory, 구성: Release Win32 ------
 
컴파일하고 있습니다.
libqopenmemory.cpp
libqopenmemory.cpp(220) : error C3861: 'random_sample_n': 인수 종속성을 조회해도 식별자를 찾을 수 없습니다.
 
빌드 로그가 "file://d:\자작 프로그램\QtOpenMemory\libQOpenMemory\Release\BuildLog.htm"에 저장되었습니다.
libQOpenMemory - 1 오류, 0 경고

그래서, 이것을 둘러가기 위해서 코드를 꽤 많이 고쳤다.
std::vector<Wordlet> tmpvec;
std::vector<Wordlet>::iterator i;
tmpvec = tmpset.wordsets;
random_shuffle(tmpvec.begin(), tmpvec.end());
i = tmpvec.begin();
i += word_count;
tmpvec.erase(i, tmpvec.end());
tmpset.wordsets = tmpvec;

간단하게 설명하면, 원본 벡터의 내용을 복사해 온 다음에 섞어서 n개 추출하고 n 이상의 부분을 지우는 식으로 작동한다. 좀 더 일반적으로 써서 함수를 만들어 보자.

std::vector<T> random_sample_n(std::vector<T> orig, int count){
   std::vector<T> retvalue;
   std::vector<T>::iterator i;
   retvalue = orig;
   random_shuffle(retvalue.begin(), retvalue.end());
   i = retvalue.begin();
   i += count;
   retvalue.erase(i, retvalue.end());
   return retvalue;
}

안타깝게도 테스트는 못 해 봤다. 본인이 템플릿을 잘 못 다루므로 T 앞에 적절한 템플릿 관련 키워드를 추가시켜 주길 바란다.

Forums: 
kewlbear의 이미지

데이터 전체를 shuffle하는 것 보다는 Programming Pearls의 A sample problem에 인용된 Knuth의 S 알고리즘을 사용하는 것이 효율적입니다.

m = c.size();
 
for (it = c.begin(); it != c.end(); ++it) {
  if (rand() % m < n) {
    result.push_back(*it);
    --n;
  }
  --m;
}