std::upper_bound/lower_bound 같은 탐색이 가장 빠른 자료구조

klara의 이미지

어떤 정렬된 값들이 주어졌을 때, 특정 값이 어디사이에 포함되는지를 가장 빠르게 찾고 싶습니다.
값들은 전부 실수(double)입니다.

예를 들어 지금은 std::map[double, T] (꺽쇠를 매번 엔티티로 적어주는게 귀찮아서 []로 적겠습니다) 를 이용하고 있는데,

std::map[double, int] map;
map[0.1] = 1;
map[0.5] = 0;
map[2.1] = 3;
...

이런식으로 들어있을 때, 0.7이란 key가 map안에서는 (0.5, 0)과 (2.1, 3)사이에 있다는 것을 찾아내기 위해서
auto it = --map.upper_bound(0.7); // it == pair(0.5, 0)
처럼 위치를 가져오고 있습니다.
보통은 이진탐색 정도면 나쁘지 않은 속도인데, 탐색자체를 짧은 시간안에 매우 자주해야되다보니 탐색시간이 무시못할게 되버렸습니다.
key검색만 하면 된다면 hash table을 써볼텐데, key와 key사이의 구간을 찾아야되서 이건 쓸수가 없네요.
수치계산에서 이용하기 때문에 무조건 빠를수록 좋습니다.
전체 계산시간에 비하면 탐색시간은 길지 않지만, 전체 시간자체가 길다보니, 이 짧은 시간이라도 어떻게 절약하고 싶습니다.

조건은

1. map이나 정렬된 vector보다 빠르게 upper_bound같은 특정 구간값을 찾는게 가능한 자료구조
2. 삽입/삭제는 처음에 초기화할때만 이루어지기 때문에 삽입/삭제는 느려도 됨
3. 검색/정렬 key는 double만 쓰기 때문에, double에 특화된 자료구조도 상관없음
4. key는 중복되지 않음
(4. 기왕이면 STL과 호환가능한 C++ template container 소스가 존재하면 좋음)

알고 계신게 있으시다면 소개부탁드립니다.

winner의 이미지

vector 구축을 하시고 sort 를 한 다음에 lower_bound, uppper_bound, equal_range 를 쓰시면 성능이 2~3배 좋을 겁니다.

klara의 이미지

사실 저게 다가 아니라, map[double, vector[double] ] 처럼 되어있어서, 안쪽에 있는 vector[double]도 마찬가지로 비슷하게 탐색하고 있습니다.
이경우는 정렬된 벡터로 std::upper_bound를 이용하고 있구요(그래서 제목을 맵에 한정시키지 않고 std::upper_bound로 했습니다).
안쪽 검색이 더 자주일어나기 때문에 이쪽은 이미 정렬된 벡터로 하고 있어서 바깥쪽 map을 vector로 바꿔도(실제로 해봐도) 시간차는 거의 없다시피합니다.
벡터나 맵이나 검색 알고리즘은 똑같은 이진탐색이니까 소요시간도 비슷할거라고 생각했는데 꼭 그런건 아닌가보군요.
아무튼 이진탐색보다 빠르게 해당범위를 찾는 방법은 없을까요?

winner의 이미지

xylosper 님이 쓰신 글인 줄 모르고 너무 쉽게 생각했네요.
가장 쉽게 생각해볼 수 있는 대안은 hash table 이죠.
그 외에 map의 lower_bound, upper_bound member 함수를 보면 iterator hint를 쓸 수 있습니다.

이거 말고는 자료구조만으로는 획기적으로 성능을 향상시킬 수 있는 방법은 쉽게 떠오르지 않을 것 같습니다.
응용에 사용되는 제약사항을 좀더 고민해보면 개선안이 나올 수도 있겠죠.

klara의 이미지

언급하신 hash table은 대소관계로 구간을 찾을수가 없지 않나요?
단순 키검색이라면 해쉬테이블을 쓸텐데, 어디 사이에 포함되는지를 찾는거라 정렬되지 않은 해쉬테이블로 가능할지 의문이네요.
해쉬함수가 대소관계도 보장해준다면 가능할려나... 하는 생각은 듭니다.
iterator hint를 활용해볼수 없을지 고민해봐야겠습니다.
답변과 과찬의 말씀 감사합니다.

winner의 이미지

만일 key가 double 이고, key 값의 간격이 그리 넓지 않고 촘촘한 경우 즉 key가 세개 있을 때
1.1, 5.5, 11.3 이런 경우 말고
1.1, 2.2, 2.3 이런 경우라면
hash 함수를 그냥 floor로 한 다음에 앞 뒤를 key 검색해보는 것도 괜찮을 것 같습니다.

Knuth가 정렬된 hash table 을 만들었다고 하던데 사용한다는 사람을 못 본 걸 보면 쓸만한 것 같지는 않고요.

klara의 이미지

괜찮아 보이네요. 적당한 변환만 생각할수 있다면, 해쉬함수대신에 그냥 vector에 넣어서 인덱스로 한방에 가는것도 가능하겠습니다.
고민좀해봐야겠네요. 정말 감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.