字典单词的随机选择
我想从数组中选择一定数量的随机单词,使总数为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个字母的单词解决的话,但随后它们更随机选择我至少希望。
rand()
function生成0
和RAND_MAX
之间的数字。
如果RAND_MAX
定义为32767
,那么您不会访问字典(数组?)中索引大于该数组的元素。
如果你需要产生比RAND_MAX
更大的随机数,再想想求和的rand()
n
调用,这样n * RAND_MAX >= dictionary.size()
结果。然后保证这个结果的模数给出一个落在整个字典范围内的索引。
添加随机数字会改变分布。 – 2012-03-04 13:25:13
均匀分布总和的分布是正态分布,这是真的。但是真正的问题在于生成的索引是在[0,RAND_MAX]而不是[0,dictionary.size() - 1]的范围内,因此并非所有的字典元素都可以访问。所以,不管用什么方法生成随机数,输出允许访问整个字典似乎很重要。 – 2012-03-04 13:32:37
那么解决了一切。 – Arnas 2012-03-04 13:32:46
即使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()
大多数实现是相当简单的,不得提供一个良好的正态分布开始。
请注意,使用模数来限制随机数的间隔会产生偏差。 – 2012-03-04 13:27:08