大多数答案都建议对初始容器进行洗牌。如果您不希望修改它,仍然可以使用此方法,但首先需要复制容器。@pmr的解决方案(因为他将其转换为函数而很好)将变为:
template <typename InputIterator, typename Size, typename OutputIterator>
void take_random_n(InputIterator first, InputIterator last,
Size n, OutputIterator result)
{
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
std::vector<value_type> shufflingVec(first, last);
std::random_shuffle(shufflingVec.begin(), shufflingVec.end());
std::copy(shufflingVec.begin(), shufflingVec.begin() + n, result);
}
然而,如果容器中包含的元素很重且复制需要一些时间,那么复制整个容器可能会非常昂贵。在这种情况下,你最好打乱索引列表:
template <typename InputIterator, typename Size, typename OutputIterator>
void take_random_n(InputIterator first, InputIterator last,
Size n, OutputIterator result)
{
typedef typename
std::iterator_traits<InputIterator>::value_type value_type;
typedef typename
std::iterator_traits<InputIterator>::difference_type difference_type;
difference_type size = std::distance(first, last);
std::vector<value_type> indexesVec(
boost::counting_iterator<size_t>(0),
boost::counting_iterator<size_t>(size));
std::random_shuffle(indexesVec.begin(), indexesVec.end());
for (Size i = 0 ; i < n ; ++i)
{
*result++ = *std::advance(first, indexesVec[i]);
}
}
你会注意到,后一种解决方案的性能表现取决于你使用的迭代器类型:对于随机访问迭代器(如指针或 vector<T>::iterator
),它会很好,但对于其他类型的迭代器,使用 std::distance
和大量调用 std::advance
可能会导致相当大的开销。