我想从数组中选择一些随机单词,使总数达到36个字母。
首先,我尝试选择一个随机单词并在检查它不长于我们拥有的可用空间量后添加它。这样效率不高,因为列表会填满,只剩下2-3个字母的空白空间,找到这么短的单词需要很长时间。
所以我决定只选择六个6个字母的单词,我这样做是通过生成一个随机数然后将其递增1直到我们找到一个6个字母的单词。它的速度非常快,但是这些词并不是那么随意,通常我会从同一个字母开始的单词或者只有以a,b,c或x,y,z为序列的单词开头的单词。
srand ( time(NULL) );
for(int i=0;i<6;i++)
{
randNumb = rand()%dictionary.size();
while(dictionary.at(randNumb).length() != 6)
{
randNumb++;
}
a << "/" << dictionary.at(randNumb) << "/";
}
我想选择不同长度但又有利于表现的单词,我只会用6个字母的单词来解决,但是我至少会希望它们随机选择。
答案 0 :(得分:3)
您应该获得一个新的随机数而不是增加索引。你这样做的方式,所有与你的标准不匹配的字符串“吸引”更多的随机数,并可能导致以下字符串被选中的概率更高。
答案 1 :(得分:0)
rand()
function会在0
和RAND_MAX
之间生成一个数字。
如果将RAND_MAX
定义为32767
,那么您将不会访问索引中大于该值的字典(数组?)中的元素。
如果您需要生成大于RAND_MAX
的随机数,请考虑对n
rand()
次n * RAND_MAX >= dictionary.size()
次调用的结果进行求和,以便{{1}}。然后保证该结果的模数给出一个落在整个字典边界某处的索引。
答案 2 :(得分:0)
即使RAND_MAX
大于dictionary.size()
,使用%
运算符选择索引也会导致分布不均匀。模数将导致比后面的单词更频繁地选择早期单词(除非RAND_MAX + 1
是dictionary.size()
的整数倍。)
考虑一个简单的例子:假设你的字典有10个单词,RAND_MAX是14.当rand()
返回0到9之间的值时,直接选择相应的单词。但是当rand()
为10到14时,则会选择前五个单词中的一个。所以前五个单词的选择几率是后五个单词的两倍。
将[0 .. RAND_MAX
]映射到[0 .. dictionary.size()
)的更好方法是使用除法:
assert(RAND_MAX + 1 >= dictionary.size());
randNumb = rand() * dictionary.size() / (RAND_MAX + 1);
但你必须小心整数溢出。如果RAND_MAX * dictionary.size()
大于您在整数中表示的值,则需要使用更大的数据类型。出于此目的,某些系统具有MulDiv
之类的功能。如果您没有MulDiv
之类的内容,则可以转换为浮点类型,然后将结果截断为整数:
double temp = static_cast<double>(rand()) * dictionary.size() / (RAND_MAX + 1);
randNumb = static_cast<int>(temp);
这仍然是一个不完美的发行版,但“热门”单词现在会在字典中均匀分布,而不是在开头聚集。
RAND_MAX + 1
越接近dictionary.size()
的整数倍,你就越好。如果你不能确定它接近整数倍,那么你希望RAND_MAX相对于dictionary.size()
尽可能大。
由于您无法控制RAND_MAX
,因此可以考虑调整dictionary.size()
。例如,如果您只想要六个字母的单词,那么为什么不将所有其他单词从字典中删除?
std::vector<std::string> six_letter_words;
std::copy_if(dictionary.begin(), dictionary.end(),
std::back_inserter(six_letter_words),
[](const std::string &word){ return word.size() == 6; });
使用简化集合,我们可以使用更通用的算法来选择单词:
typedef std::vector<std::string> WordList;
// Returns true with the given probability, which should be 0.0 to 1.0.
bool Probably(double probability) {
return (static_cast<double>(std::rand()) / RAND_MAX) < probability;
}
// Selects n words from the dictionary using a normal distribution and
// copies them to target.
template <typename OutputIt>
OutputIt Select(int n, const WordList &dictionary, OutputIt target) {
double count = static_cast<double>(n);
for (std::size_t i = 0; count > 0.0 && i < dictionary.size(); ++i) {
if (Probably(count / (dictionary.size() - i))) {
*target++ = dictionary[i];
count -= 1.0;
}
}
return target;
}
这个想法是逐步浏览字典中的每个单词并选择它,你需要选择的单词数量除以剩下的单词数量。即使RAND_MAX
相对较小,这也很有效。但总的来说,它比试图随机选择索引要多得多。另请注意,此技术将永远不会多次选择相同的单词,其中索引映射技术可以。
您可以像这样致电Select
:
// Select six words from six_letter_words using a normal distribution.
WordList selected;
Select(6, six_letter_words, std::back_inserter(selected));
另请注意,rand()
的大多数实现都非常简单,并且可能无法提供良好的正态分布。