问题描述
我需要在很小的范围内生成随机数(有时只有0-1,即抛硬币)。分配的准确性不是特别重要,但是我确实需要避免相同数目的长序列。
我尝试使用C ++ 11 std :: uniform_int_distribution生成随机数,虽然分布非常好,但是它可以连续生成15个以上具有单个值的序列(请注意,我实际上并没有-每次都播入RNG)。
int randomInRange(int range)
{
std::mt19937 rng(0);
auto seed = std::random_device{}();
rng.seed(seed);
std::uniform_int_distribution<int> dist(0,range - 1);
return dist(rng);
}
我构建了一个测试程序(https://ideone.com/f9p0WJ),该程序显示它可以连续生成多达18个磁头。我想将概率降低到均匀分布之外,例如将运行3的概率减半,将运行5的概率减半。
是否有一个通用的解决方案?我的幼稚解决方案是保留一些历史记录,并在我检测到一个序列太长(概率小于1)时将其丢弃,但是也许比我聪明的人已经想到了这一点?
解决方法
您追求的不是纯粹的均匀分布,就像@Henry说的那样。
为加强您的约束,我认为最好的解决方案是在您的随机数生成器中包括一个衰减因子。随着数字序列的增加,下一个出现该数字的可能性降低。
我在python 3中实现了一些原型代码,因为目前我的cpp技能还有些生疏,但是基本概念很容易转换为cpp。在这里:
def my_random(range: int,iterations: int,decay_rate :float = 2) -> List[int]:
assert range > 0,"`range` must be a positive non-zero integer"
if range == 1:
return [0] * iterations
last_num: int = 0
last_prob: float = 1/range
rand_num_lst: List[int] = []
while iterations > 0:
rnd = random() # generates a random number: 0 <= rnd < 1
if rnd < last_prob:
num = last_num
last_prob /= decay_rate
else:
# The `int` function is converting the float into integer by
# flooring the number
num = int( (rnd - last_prob) / (1 - last_prob) * (range - 1) )
if num >= last_num:
num += 1
last_num = num
last_prob = 1/range/decay_rate
rand_num_lst.append(num)
iterations -= 1
return rand_num_lst
请注意,在Python3中,默认除法是浮点除法,这意味着1/2 = 0.5
代替了Python2中的1/2 = 0
。
我进行了一些测试,以检查最大序列长度以及由此生成的数字的分布是否仍均匀分布,并且似乎继续保持这些属性:
以range = 2
和不同的衰减率运行:
decay_rate: 2.00000 max sequence length: 6 number count: {0: 499830,1: 500170}
decay_rate: 1.50000 max sequence length: 6 number count: {0: 499455,1: 500545}
decay_rate: 1.25000 max sequence length: 9 number count: {0: 500241,1: 499759}
decay_rate: 1.12500 max sequence length: 11 number count: {0: 499799,1: 500201}
decay_rate: 1.06250 max sequence length: 14 number count: {0: 500655,1: 499345}
decay_rate: 1.03125 max sequence length: 16 number count: {0: 500495,1: 499505}
decay_rate: 1.01562 max sequence length: 16 number count: {0: 500010,1: 499990}
decay_rate: 1.00781 max sequence length: 18 number count: {0: 499748,1: 500252}
decay_rate: 1.00391 max sequence length: 18 number count: {0: 499987,1: 500013}
decay_rate: 1.00195 max sequence length: 21 number count: {0: 499503,1: 500497}
decay_rate: 1.00098 max sequence length: 21 number count: {0: 500495,1: 499505}
decay_rate: 1.00000 max sequence length: 19 number count: {0: 499451,1: 500549}
以range = 5
和不同的衰减率运行:
decay_rate: 2.00000 max sequence length: 5 number count: {0: 200314,1: 199245,2: 200213,3: 199962,4: 200266}
decay_rate: 1.50000 max sequence length: 5 number count: {0: 199372,1: 199829,2: 199937,3: 200527,4: 200335}
decay_rate: 1.25000 max sequence length: 6 number count: {0: 199373,1: 199784,2: 200561,3: 200062,4: 200220}
decay_rate: 1.12500 max sequence length: 8 number count: {0: 199752,1: 199931,2: 200579,3: 200287,4: 199451}
decay_rate: 1.06250 max sequence length: 8 number count: {0: 199280,1: 200286,2: 199688,3: 200446,4: 200300}
decay_rate: 1.03125 max sequence length: 8 number count: {0: 199577,1: 199582,2: 200652,3: 199870,4: 200319}
decay_rate: 1.01562 max sequence length: 9 number count: {0: 200442,1: 199916,2: 200142,3: 199729,4: 199771}
decay_rate: 1.00781 max sequence length: 9 number count: {0: 199784,1: 200544,2: 199921,3: 199557,4: 200194}
decay_rate: 1.00391 max sequence length: 9 number count: {0: 199920,1: 199054,2: 200303,3: 200833,4: 199890}
decay_rate: 1.00195 max sequence length: 9 number count: {0: 200011,1: 200530,2: 199806,3: 200321,4: 199332}
decay_rate: 1.00098 max sequence length: 10 number count: {0: 199741,1: 199861,2: 199822,3: 200081,4: 200495}
decay_rate: 1.00000 max sequence length: 9 number count: {0: 199717,1: 199184,2: 200182,3: 200891,4: 200026}
当然,您可以明确地编写类似以下内容的代码:如果运行序列的长度大于X
,则只需忽略该数字并生成与最后一个随机数不同的数字即可。尽管我不确定这种方法是否会继续均匀分布。
这是我的@MkWTF答案的C ++实现,具有与C ++ 11 std :: uniform_int_distribution兼容的接口。 (它需要reset()和其他元素才能成为完整的C ++ 11 RandomNumberDistribution
。)
#include <random>
class decaying_sequence_distribution
{
private:
const int min;
const int range;
const double decay_rate;
std::uniform_real_distribution<> dist{0.,1.};
int last_num;
double last_prob;
public:
decaying_sequence_distribution(int min_,int max_,double decay_rate_ = 2.)
: min(min_),range(max_ - min_ + 1),decay_rate(decay_rate_),last_num(min_),last_prob(1. / range)
{
}
template<class Generator>
int operator()(Generator& g)
{
int num;
double rnd = dist(g);
if (rnd < last_prob)
{
num = last_num;
last_prob /= decay_rate;
}
else
{
num = static_cast<int>( (rnd - last_prob) / (1 - last_prob) * (range - 1) );
if (num >= last_num)
num += 1;
last_num = num;
last_prob = 1./range/decay_rate;
}
return num + min;
}
};