C++:如何在从给定缓存中排除数字的同时生成随机数

C++: How to generate random numbers while excluding numbers from a given cache

本文关键字:数字 排除 随机数 缓存 C++      更新时间:2023-10-16

所以在 c++ 中,我使用 mt19937引擎和随机数生成器中的uniform_int_distribution,如下所示:

#include <random>
#include <time.h>
int get_random(int lwr_lm, int upper_lm){
std::mt19937 mt(time(nullptr));
std::uniform_int_distribution<int> dist(lwr_lm, upper_lm);
return dist(mt);
}

我需要的是更改上述生成器,以便有一个包含许多整数的缓存,当我一遍又一遍地使用上述生成器时,我需要排除这些整数。 我如何更改上述内容以实现此目的?

有很多方法可以做到这一点。一个简单的方法是在std::set中维护您的"排除数",并在每次生成随机数后,检查它是否在集合中,如果它,然后生成一个新的随机数 - 重复直到你得到一个不在集合中的数字,然后返回它。

顺便说一句;虽然发行版的构造成本很低,但引擎却不便宜。您不希望每次调用函数时都重新构造mt19937,而是创建一次,然后重用它。您可能还希望使用比当前时间更好的种子(以秒为单位(。

您是否 1( 尝试在离散区间内不替换的情况下进行采样?还是 2( 区间内的斑块分布,表示相当恒定?

如果 1( 你可以按照这里的答案使用 std::shuffle 如何使用 c++ uniform_int_distribution 进行采样而不替换

如果 2( 您可以使用 std::d iscrete_distribution(元素 0 对应于lwr_lm(并将您不想要的数字加权归零。显然,内存要求在upper_lm-lwr_lm是线性的,因此如果这很大,则可能不切实际

我会为这个问题提出两个类似的解决方案。它们基于概率结构,并为您提供"可能在缓存中"或"绝对不在缓存中"的答案。有误报,但没有漏报。

  1. 完美的哈希函数。有许多实现,包括一个来自GNU的实现。基本上,在一组缓存值上运行它,并使用生成的完美哈希函数来拒绝采样值。您甚至不需要维护哈希表,只需将随机值映射到整数索引的函数即可。一旦索引在哈希范围内,就拒绝该数字。完美意味着您只需要一个电话即可检查,结果会告诉您该号码在集合中。存在潜在的冲突,因此可能会出现误报。

  2. 布隆过滤器。同样的想法,使用您愿意备用的每个缓存项目的任何位构建过滤器,并通过快速检查,您将获得"缓存中可能"的答案或清除否定。你可以用答案精度换取记忆,反之亦然。误报是可能的

正如@virgesmith在他的回答中提到的,根据您的问题,这可能是更好的解决方案。
带有缓存并使用它来过滤未来生成的方法对于大范围维基来说是低效的。

在这里,我用不同的方法写了一个幼稚的例子,但你会受到记忆的限制。为缓冲区选择随机数并将其删除以进行下一次迭代。

#include <random>
#include <time.h>
#include <iostream>
int get_random(int lwr_lm, int upper_lm, std::vector<int> &buff, std::mt19937 &mt){
if (buff.size() > 0) {
std::uniform_int_distribution<int> dist(0, buff.size()-1);
int tmp_index = dist(mt);
int tmp_value = buff[tmp_index];
buff.erase(buff.begin() + tmp_index);
return tmp_value;
} else {
return 0;
}
}
int main() {
// lower and upper limit for random distribution
int lower = 0;
int upper = 10;
// Random generator
std::mt19937 mt(time(nullptr));
// Buffer to filter and avoid duplication, Buffer contain all integer between lower and uper limit
std::vector<int> my_buffer(upper-lower);
std::iota(my_buffer.begin(), my_buffer.end(), lower);
for (int i = 0; i < 20; ++i) {
std::cout << get_random(lower, upper, my_buffer, mt) << std::endl;
}
return 0;
} 

编辑:这里有一个更清洁的解决方案

这可能不是最漂亮的解决方案,但是是什么阻止您在返回之前维护该缓存并检查是否存在?不过,对于大型缓存,它会变慢。

#include <random>
#include <time.h>
#include <set>
std::set<int> cache;
int get_random(int lwr_lm, int upper_lm){
std::mt19937 mt(time(nullptr));
std::uniform_int_distribution<int> dist(lwr_lm, upper_lm);
auto r = dist(mt);
while(cache.find(r) != cache.end())
r = dist(mt);
return r;
}