std::upper_bound/lower_bound 같은 탐색이 가장 빠른 자료구조
어떤 정렬된 값들이 주어졌을 때, 특정 값이 어디사이에 포함되는지를 가장 빠르게 찾고 싶습니다.
값들은 전부 실수(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 소스가 존재하면 좋음)
알고 계신게 있으시다면 소개부탁드립니다.
vector와 sort
vector 구축을 하시고 sort 를 한 다음에 lower_bound, uppper_bound, equal_range 를 쓰시면 성능이 2~3배 좋을 겁니다.
사실 저게 다가 아니라, map[double,
사실 저게 다가 아니라, map[double, vector[double] ] 처럼 되어있어서, 안쪽에 있는 vector[double]도 마찬가지로 비슷하게 탐색하고 있습니다.
이경우는 정렬된 벡터로 std::upper_bound를 이용하고 있구요(그래서 제목을 맵에 한정시키지 않고 std::upper_bound로 했습니다).
안쪽 검색이 더 자주일어나기 때문에 이쪽은 이미 정렬된 벡터로 하고 있어서 바깥쪽 map을 vector로 바꿔도(실제로 해봐도) 시간차는 거의 없다시피합니다.
벡터나 맵이나 검색 알고리즘은 똑같은 이진탐색이니까 소요시간도 비슷할거라고 생각했는데 꼭 그런건 아닌가보군요.
아무튼 이진탐색보다 빠르게 해당범위를 찾는 방법은 없을까요?
이래서 글쓴이를 확인해야...
xylosper 님이 쓰신 글인 줄 모르고 너무 쉽게 생각했네요.
가장 쉽게 생각해볼 수 있는 대안은 hash table 이죠.
그 외에 map의 lower_bound, upper_bound member 함수를 보면 iterator hint를 쓸 수 있습니다.
이거 말고는 자료구조만으로는 획기적으로 성능을 향상시킬 수 있는 방법은 쉽게 떠오르지 않을 것 같습니다.
응용에 사용되는 제약사항을 좀더 고민해보면 개선안이 나올 수도 있겠죠.
언급하신 hash table은 대소관계로 구간을
언급하신 hash table은 대소관계로 구간을 찾을수가 없지 않나요?
단순 키검색이라면 해쉬테이블을 쓸텐데, 어디 사이에 포함되는지를 찾는거라 정렬되지 않은 해쉬테이블로 가능할지 의문이네요.
해쉬함수가 대소관계도 보장해준다면 가능할려나... 하는 생각은 듭니다.
iterator hint를 활용해볼수 없을지 고민해봐야겠습니다.
답변과 과찬의 말씀 감사합니다.
hash table 을 쓰면 대략 이런게 가능하겠죠.
만일 key가 double 이고, key 값의 간격이 그리 넓지 않고 촘촘한 경우 즉 key가 세개 있을 때
1.1, 5.5, 11.3 이런 경우 말고
1.1, 2.2, 2.3 이런 경우라면
hash 함수를 그냥 floor로 한 다음에 앞 뒤를 key 검색해보는 것도 괜찮을 것 같습니다.
Knuth가 정렬된 hash table 을 만들었다고 하던데 사용한다는 사람을 못 본 걸 보면 쓸만한 것 같지는 않고요.
괜찮아 보이네요. 적당한 변환만 생각할수 있다면,
괜찮아 보이네요. 적당한 변환만 생각할수 있다면, 해쉬함수대신에 그냥 vector에 넣어서 인덱스로 한방에 가는것도 가능하겠습니다.
고민좀해봐야겠네요. 정말 감사합니다.
댓글 달기