可能重复:
Unique random numbers in O(1)?
Unique random numbers in an integer array in the C programming language
我有std::vector
个不确定大小的独特元素。我想从这个向量中获取20个唯一且随机的元素。 “独特”我的意思是我不想多次获取相同的索引。目前我这样做的方式是致电std::random_shuffle
。但这需要我将整个矢量(可能包含1000多个元素)混洗。我不介意改变向量(我不喜欢,因为我不需要使用线程锁),但最重要的是我希望它有效。我不应该超过我的需要。
请注意,我已经研究了将部分范围传递给std::random_shuffle
,但它只会洗掉那个元素子集,这意味着该范围之外的元素永远不会被使用!
帮助表示赞赏。谢谢!
注意:我正在使用Visual Studio 2005,因此我无法访问C ++ 11的功能和库。
答案 0 :(得分:8)
您可以使用Fisher Yates http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Fisher-Yates shuffle(以Ronald Fisher和Frank Yates命名),也称为Knuth shuffle(在Donald Knuth之后),是一种用于生成有限集合的随机排列的算法,用于随机改组集合。 Fisher-Yates shuffle的变体(称为Sattolo算法)可用于生成长度为n的随机循环。正确实施,Fisher-Yates shuffle是公正的,因此每个排列都是同样可能的。该算法的现代版本也相当有效,只需要与被洗牌的项目数量成比例的时间,而不需要额外的存储空间。 Fisher-Yates洗牌的基本过程类似于从帽子中随机挑选编号的门票,或从牌组中随机挑选牌,直到不再剩下。具体算法提供的是一种以有效和严谨的方式在数字上进行此操作的方法,正确完成后,保证了无偏见的结果。
我认为这个伪代码应该有用(有可能出现一个错误或者其他东西,所以请仔细检查它!):
std::list chosen; // you don't have to use this since the chosen ones will be in the back of the vector
for(int i = 0; i < num; ++i) {
int index = rand_between(0, vec.size() - i - 1);
chosen.push_back(vec[index]);
swap(vec[index], vec[vec.size() - i - 1]);
}
答案 1 :(得分:8)
您想要从n向量中随机抽取大小为m的样本:
让rand(a)返回0..a-1 uniform
for (int i = 0; i < m; i++)
swap(X[i],X[i+rand(n-i)]);
X[0..m-1]
现在是一个随机样本。
答案 2 :(得分:3)
使用循环将随机索引编号放入std::set
,并在size()
达到20时停止。
std::set<int> indexes;
std::vector<my_vector::value_type> choices;
int max_index = my_vector.size();
while (indexes.size() < min(20, max_index))
{
int random_index = rand() % max_index;
if (indexes.find(random_index) == indexes.end())
{
choices.push_back(my_vector[random_index]);
indexes.insert(random_index);
}
}
随机数生成是我头脑中的第一件事,随时可以使用更好的东西。
答案 3 :(得分:0)
#include <iostream>
#include <vector>
#include <algorithm>
template<int N>
struct NIntegers {
int values[N];
};
template<int N, int Max, typename RandomGenerator>
NIntegers<N> MakeNRandomIntegers( RandomGenerator func ) {
NIntegers<N> result;
for(int i = 0; i < N; ++i)
{
result.values[i] = func( Max-i );
}
std::sort(&result.values[0], &result.values[0]+N);
for(int i = 0; i < N; ++i)
{
result.values[i] += i;
}
return result;
};
使用示例:
// use a better one:
int BadRandomNumberGenerator(int Max) {
return Max>4?4:Max/2;
}
int main() {
NIntegers<100> result = MakeNRandomIntegers<100, 500>( BadRandomNumberGenerator );
for (int i = 0; i < 100; ++i) {
std::cout << i << ":" << result.values[i] << "\n";
}
}
使每个数字1的最大值小于最后一个。对它们进行排序,然后将每个值提高到它之前的整数数量。
模板的东西只是商业外观。