functional.h
Go to the documentation of this file.
1/*!
2 *
3 *
4 * \brief Small General algorithm collection.
5 *
6 *
7 *
8 *
9 * \author Oswin Krause
10 * \date 2012
11 *
12 *
13 * \par Copyright 1995-2017 Shark Development Team
14 *
15 * <BR><HR>
16 * This file is part of Shark.
17 * <https://shark-ml.github.io/Shark/>
18 *
19 * Shark is free software: you can redistribute it and/or modify
20 * it under the terms of the GNU Lesser General Public License as published
21 * by the Free Software Foundation, either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * Shark is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU Lesser General Public License for more details.
28 *
29 * You should have received a copy of the GNU Lesser General Public License
30 * along with Shark. If not, see <http://www.gnu.org/licenses/>.
31 *
32 */
33#ifndef SHARK_CORE_FUNCTIONAL_H
34#define SHARK_CORE_FUNCTIONAL_H
35
36#include <boost/range/numeric.hpp>
38#include <algorithm>
39#include <shark/Core/Random.h>
40namespace shark{
41
42///\brief random_shuffle algorithm which stops after acquiring the random subsequence for [begin,middle)
43template<class Iterator, class Rng>
44void shuffle(Iterator begin, Iterator end, Rng& rng){
45 using std::swap;
46 Iterator next = begin;
47 for (std::size_t index = 1; ++next != end; ++index){
48 swap(*next, *(begin + random::discrete(rng, std::size_t(0),index)));
49 }
50}
51
52
53///\brief random_shuffle algorithm which stops after acquiring the random subsequence for [begin,middle)
54template<class RandomAccessIterator, class Rng>
55void partial_shuffle(RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end, Rng& rng){
56 shark::shuffle(begin,end,rng);
57 // todo: test the algorithm below!
58 //~ typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
59 //~ difference_type n = middle - begin;
60 //~ for (; begin != middle; ++begin,--n) {
61
62 //~ using std::swap;
63 //~ swap(*begin, begin[rng(n)]);
64 //~ }
65}
66
67
68
69///\brief random_shuffle algorithm which stops after acquiring the random subsequence for [begin,middle)
70template<class RandomAccessIterator>
71void partial_shuffle(RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end){
72 partial_shuffle(begin,middle,end,random::globalRng);
73}
74
75///\brief Returns the iterator to the median element. after this call, the range is partially ordered.
76///
77///After the call, all elements left of the median element are
78///guaranteed to be <= median and all element on the right are >= median.
79template<class Range>
80typename boost::range_iterator<Range>::type median_element(Range& range){
81 std::size_t size = range.size();
82 std::size_t medianPos = (size+1)/2;
83 auto medianIter = boost::begin(range)+medianPos;
84
85 std::nth_element(range.begin(),medianIter, range.end());
86
87 return medianIter;
88}
89
90template<class Range>
91typename boost::range_iterator<Range>::type median_element(Range const& rangeAdaptor){
92 Range adaptorCopy(rangeAdaptor);
93 return median_element(adaptorCopy);
94}
95/// \brief Partitions a range in two parts as equal in size as possible.
96///
97/// The Algorithm partitions the range and returns the splitpoint. The elements in the range
98/// are ordered such that all elements in [begin,splitpoint) < [splitpoint,end).
99/// This partition is done such that the ranges are as equally sized as possible.
100/// It is guaranteed that the left range is not empty. However, if the range consists only
101/// of equal elements, the return value will be the end iterator indicating that there is no
102/// split possible.
103/// The whole algorithm runs in linear time by iterating 2 times over the sequence.
104template<class Range>
105typename boost::range_iterator<Range>::type partitionEqually(Range& range){
106 auto begin = range.begin();
107 auto end = range.end();
108 auto medianIter = median_element(range);
109
110 // in 99% of the cases we would be done right now. in the remaining 1% the median element is
111 // not unique so we partition the left and the right such that all copies are ordered in the middle
112 auto median = *medianIter;
113 typedef typename Range::const_reference const_ref;
114 auto left = std::partition(begin,medianIter,[&](const_ref elem){return elem < median;});
115 auto right = std::partition(medianIter,end,[&](const_ref elem){return elem == median;});
116
117 // we guarantee that the left range is not empty
118 if(left == begin){
119 return right;
120 }
121
122 // now we return left or right, maximizing size balance
123 if (left - begin >= end - right)
124 return left;
125 else
126 return right;
127}
128
129///\brief Partitions a range in two parts as equal in size as possible and returns it's result
130///
131///This the verison for adapted ranges.
132template<class Range>
133typename boost::range_iterator<Range>::type partitionEqually(Range const& rangeAdaptor){
134 Range adaptorCopy(rangeAdaptor);
135 return partitionEqually(adaptorCopy);
136}
137
138}
139#endif