|
Boost : |
From: Ross MacGregor (ross__macgregor_at_[hidden])
Date: 2003-10-09 18:27:35
I have discovered a fundamental mutating algorithm that I think should
be a standard STL algorithm. It is somewhat tricky to implement (not on
the order of partial_sort, I'd admit) and complements the other existing
algorithms.
It is partial_random_shuffle(). It is an optimization of
random_shuffle() if you would like a subset of random elements from a
container.
It would have a similar interface to partial_sort() and in many ways
performs the inverse operation.
sort (beg, end, op) // sorts a container
random_shuffle(beg, end, op) // unsorts a container
partial_sort (beg, sort_end, end, op) // sorts N elements
partial_random_shuffle(beg, shuffle_end, end, op) // unsorts N elements
I suppose for many applications performing a random_shuffle() on the
entire container may be acceptable, but I like to use optimized
algorithms where-ever possible.
Is there any interest in seeing this as a boost algoritm?
Here is my implementation:
--------------------------
#ifndef BOOST_PARTIAL_RANDOM_SHUFFLE_HPP_INCLUDED
#define BOOST_PARTIAL_RANDOM_SHUFFLE_HPP_INCLUDED
#include <cstdlib>
namespace boost {
// This is a variation of the random_shuffle algorithm
// that allows you to partially shuffle an STL container.
// Elements from the sub-range ("p" to "shuffle_end") will
// be swapped with random elements from the entire range
// ("p" to "end").
//
// Usage:
// // We have 1000 things to index
// vector<int> indices;
// for(int i=0; i<1000; i++) indices.push_back(i);
// // select 100 random indices
// partial_random_shuffle(indices.begin(), indices.begin() + 100,
indices.end());
// indices.resize(100);
// Contributed by Ross MacGregor
template<class RandomAccessIter>
inline void
partial_random_shuffle(
RandomAccessIter p,
RandomAccessIter shuffle_end,
RandomAccessIter end)
{
// find number of elements to the end
size_t range = std::distance(p, end);
// check for empty range
if( range != 0 )
{
// unable to swap last element
if( shuffle_end == end )
--shuffle_end;
// must have more than 1 element
// for swap operation to work
if( --range )
{
while( p != shuffle_end )
{
size_t n( rand() % range );
std::swap(*p,*(p + n));
--range;
++p;
}
}
}
}
template<class RandomAccessIter, class RandomNumberGenerator>
inline void
partial_random_shuffle(
RandomAccessIter p,
RandomAccessIter shuffle_end,
RandomAccessIter end,
RandomNumberGenerator & rand_func)
{
// find number of elements to the end
size_t range = std::distance(p, end);
// check for empty range
if( range != 0 )
{
// unable to swap last element
if( shuffle_end == end )
--shuffle_end;
// must have more than 1 element
// for swap operation to work
if( --range )
{
while( p != shuffle_end )
{
size_t n( rand_func(range) );
std::swap(*p,*(p + n));
--range;
++p;
}
}
}
}
} // namespace boost
#endif // BOOST_PARTIAL_RANDOM_SHUFFLE_HPP_INCLUDED
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk