/* This is a one-pass version of the selection algorithm (LazySelect) described in [Rajeev Motwani and Prabhakar Raghavan, Randomized Algorithms, Cambridge University Press (1995)]. Authors: Jesper Bojesen, Jyrki Katajainen, Lars Yde © 1999, 2002, 2011 */ #ifndef __CPHSTL_RANDOMIZED_POSITIONER__ #define __CPHSTL_RANDOMIZED_POSITIONER__ #include // std::min, std::max #include // ilogb #include // std::rand #include // std::binary_function #include // std::iterator_traits #include // std::vector #include // std::pair extern int ilogb(double) throw(); namespace cphstl { namespace { template void tagged_sampler(R first, R past, std::vector >& sample, integer n) { assert(sample.empty()); typename std::vector >::iterator i; integer N = past - first; assert(RAND_MAX >= N); for (integer i = 0; i != n; ++i) { integer rnd = std::rand() % N; sample.push_back(std::pair(*(first + rnd), first + rnd)); } } template class tagged_relation : public std::binary_function, std::pair, bool> { public: typedef std::pair argument_type; explicit tagged_relation(C const& f) : less(f) { } bool operator() (argument_type const& x, argument_type const& y) const { return less(x.first, y.first)/* || (!less(y.first, x.first) && x.second < y.second)*/; } protected: C less; }; template std::pair maximum(I first, I past, C less) { assert(first != past); E max = (*first).first; I location = first; I q = first; for (++q; q != past; ++q) { E x = (*q).first; if (! less(x, max)) { max = x; location = q; } } return *location; } template std::pair minimum(I first, I past, C less) { assert(first != past); E min = (*first).first; I location = first; I q = first; for (++q; q != past; ++q) { E x = (*q).first; if (less(x, min)) { min = x; location = q; } } return *location; } template void two_way_partitioner(R first, R past, R p, C const& less) { typename std::iterator_traits::value_type piv(*p); std::swap(*p, *(past-1)); R store = first; for(R it=first; it!=past-1; ++it) { if (less(*it, piv)) { std::swap(*store, *it); ++store; } } std::swap(*(past-1), *store); } template void rotate(R a, R b, R c) { typedef typename std::iterator_traits::value_type E; E tmp = *a; *a = *b; *b = *c; *c = tmp; } template std::pair three_way_partitioner(R first, R past, std::pair pivot1, std::pair pivot2, C const& less) { R less_p1 = first; R less_p2 = first; R greater_p2 = past - 1; R greater_p1 = past - 1; while (true) { while (less(std::pair(*less_p1, less_p1), pivot1)) { less_p1++; } while (less(pivot2, std::pair(*greater_p2, greater_p2))) { greater_p2--; } if (less_p1 >= greater_p2) { break; } if (less(pivot2, std::pair(*less_p1, less_p1))) { if (less(std::pair(*greater_p2, greater_p2), pivot1)) { std::iter_swap(less_p1++, greater_p2--); } else { rotate(greater_p2--, less_p1++, less_p2++); } } else { if (less(std::pair(*greater_p2, greater_p2), pivot1)) { rotate(less_p1++, greater_p2--, greater_p1--); } else { std::iter_swap(greater_p1--, greater_p2--); std::iter_swap(less_p1++, less_p2++); } } } if (less_p1 == greater_p2) { less_p1++; greater_p2--; }; std::swap_ranges(first, std::min(less_p2, greater_p2 + 1 - (less_p2 - first)), std::max(less_p2, greater_p2 + 1 - (less_p2 - first))); std::swap_ranges(less_p1, std::min(greater_p1 + 1, past - (greater_p1 + 1 - less_p1)), std::max(greater_p1 + 1, past - (greater_p1 + 1 - less_p1))); return std::make_pair(greater_p2 + 1 - (less_p2 - first), past - (greater_p1 + 1 - less_p1)); } template void show(I first, I past) { for (I p = first; p != past; ++p) { std::cout << *p << " "; } std::cout << std::endl; } } template void randomized_positioner(R first, R past, integer k, C const& less) { typedef typename std::iterator_traits::value_type E; typedef std::pair tagged_element; tagged_relation tagged_less = tagged_relation(less); integer n = past - first; if (n < 8) { std::sort(first, past, less); return; } integer fourth_root = powl(n,1.0/4.0); integer square_root = powl(n,1.0/2.0); integer sample_size = powl(n,3.0/4.0); std::vector sample; tagged_sampler(first, past, sample, sample_size); if (k <= fourth_root) { tagged_element p = maximum(sample.begin(), sample.end(), less); R middle = first + sample_size; --middle; std::swap(*p.second, *middle); two_way_partitioner(first, past, middle, less); std::sort(first, middle, less); } else if (k >= (n - fourth_root)) { tagged_element p = minimum(sample.begin(), sample.end(), less); R middle = past - sample_size; std::swap(*p.second, *middle); two_way_partitioner(first, past, middle, less); std::sort(middle, past, less); } else { integer x = k / fourth_root; integer ell = std::max(1, x - square_root); integer hoo = std::min(x + square_root, sample_size-1); typedef typename std::vector::iterator I; std::sort(sample.begin(), sample.end(), tagged_less); tagged_element a = sample[ell]; tagged_element b = sample[hoo]; std::pair m = three_way_partitioner(first, past, a, b, tagged_less); if (k < (m.first - first)) { std::sort(first, m.first, less); } else if (k > (m.second - first)) { std::sort(m.second, past, less); } else { std::sort(m.first, m.second, less); } } } } #endif