如何在std::set
中选择随机元素?
我天真地尝试过这个:
int GetSample(const std::set<int>& s) {
double r = rand() % s.size();
return *(s.begin() + r); // compile error
}
但是operator+
不允许这样做。
答案 0 :(得分:42)
您可以使用std::advance
方法。
#include <set>
#include <algorithm>
int main() {
using namespace std;
// generate a set...
set<int> s;
for( int i = 0; i != 10; ++i ) s.insert(i);
set<int>::const_iterator it(s.begin());
// 'advance' the iterator 5 times
advance(it,5);
}
答案 1 :(得分:2)
如果随机访问很重要,并且您可以使用O(N)平均插入次数,那么this paper中给出的解决方法可能很方便。
主要思想是使用排序向量,然后查找函数std::lower_bound
。这样,查找采用O(log N)就像在普通集中一样。此外,(随机)插入需要O(N),因为所有后续元素必须像正常向量一样被移位(并且可能执行重新分配)。但是,后面的插入是不变的(重新分配除外。您可以通过调用具有足够大存储空间的reserve()
来避免这种情况)。
最后,问题的要点:随机访问是O(1)。只需从i
中的统一分布中抽取一个随机数[0, V.size()-1]
,然后返回相应的元素V[i]
。
这是本文的代码基础,它实现了这个有序向量。根据需要进行扩展:
template <class T, class Compare = std::less<T> >
struct sorted_vector {
using std::vector;
using std::lower_bound;
vector<T> V;
Compare cmp;
typedef typename vector<T>::iterator iterator;
typedef typename vector<T>::const_iterator const_iterator;
iterator begin() { return V.begin(); }
iterator end() { return V.end(); }
const_iterator begin() const { return V.begin(); }
const_iterator end() const { return V.end(); }
//...if needed, implement more by yourself
sorted_vector(const Compare& c = Compare()) : V(), cmp(c) {}
template <class InputIterator>
sorted_vector(InputIterator first, InputIterator last, Const Compare& c = Compare())
: V(first, last), cmp(c)
{
std::sort(begin(), end(), cmp);
}
//...
iterator insert(const T& t) {
iterator i = lower_bound(begin(), end(), t, cmp);
if (i == end() || cmp(t, *i))
V.insert(i, t);
return i;
}
const_iterator find(const T& t) const {
const_iterator i = lower_bound(begin(), end(), t, cmp);
return i == end() || cmp(t, *i) ? end() : i;
}
};
对于更复杂的实现,您可能还会考虑this page。
编辑:或者甚至更好,使用boost::container::flat_set
,它使用上面的想法实现集合,即作为有序向量。
答案 2 :(得分:2)
在上面的评论中假设,它可以在 O(log(n))(对于std::advance
的vs O(n))中完成一个向量(使用 O(n)更多空间)使用我描述的方法here。
基本上,你:
it
*(it++)
获取随机元素*(set.begin())
或it
n.b:正如 Aaron 指出的那样,元素不会随机选择统一。您需要使用与集合中的元素相同的分布来构建随机元素,以接近统一轮询。
davidhigh 已经给出了带矢量的解决方案,但是有一个问题,因为当你弹出你的堆栈的一个元素时,你将不得不在< em> O(n)或者您可以在每次要检索随机元素时重建矢量,但也可以 O(n)。
要避免此问题并将插入/删除保留到 O(log n),您可以保留std::unordered_set
并使用similar method来获取第一个解决方案 O(1)中的随机元素。
p.s:如果你的元素很大,你可以使用一组无序的指针(带有修改的hasher)来节省一些内存。
答案 3 :(得分:1)
int GetSample(const std::set<int>& s) {
double r = rand() % s.size();
std::set<int>::iterator it = s.begin();
for (; r != 0; r--) it++;
return *it;
}
会是这样做的一种方式,虽然不是很漂亮;
答案 4 :(得分:1)
要从集合中获取随机元素,请首先使用rand()函数获取一个随机数,然后根据集合大小获取一个模数(%),以使我们的迭代器不会超出范围。现在,要获得随机元素,只需对idx = rand()%s.size()进行迭代次数即可获得随机元素。这种方法中每个元素都有相同的发生概率。
// making set
unordered_set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
// logic
int idx = rand()%s.size();
auto it = s.begin();
for (int i = 0; i < idx; i++)
{
it++;
}
return *it;
答案 5 :(得分:0)
C ++ 17 $query = 'SELECT *
FROM user
WHERE 1 = 1 ';
if(!empty($serviceNumber) )
{
$query .= ' and serviceNumber = ? ';
$params[] = $serviceNumber;
}
if(!empty($name))
{
$query .= ' and userName = ? ';
$params[] = $name;
}
if(!empty($status))
{
$query .= ' and status = ? ';
$params[] =$status;
}
if(!empty($params))
{
$sth->execute($params);
$dbh->prepare($query);
$result= mysqli_query($dbcon,$sth);
if(!$result )
{
die('Could not get data: ' . mysqli_error());
}
}
else
{
echo 'Missing Values';
}
这将是一种方便的,但效率不高的(O(n))方法:
std::sample
但我认为,为了提高效率,您只需复制到另一种类型的结构:How to select a random element in std::set in less than O(n) time?