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

时间:2018-08-12 14:48:24

标签: c++ random

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

// Heavy work dispatched to a separate thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        temp = [ayatArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(self.SratNo == %@)",mood]];

        NSSet *uniqueSet = [NSSet setWithArray:[temp valueForKey:@"Para"]];
        NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES];
        uniqueParasArray = [uniqueSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];

        currentParaNum = [uniqueParasArray[i] integerValue];
        NSNumber *mood = @(currentParaNum);

        NSArray *paraWiseAyats = [[NSArray alloc] init];
        paraWiseAyats = [temp filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(self.Para == %@)",mood]];



        ayatArrayForTableView = [paraWiseAyats mutableCopy];
        [self.translationTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];

});

我需要的是改变上面的生成器,以便有一个包含许多整数的缓存,当我反复使用上面的生成器时,我需要将其排除在外。 如何更改以上内容以实现此目的?

5 个答案:

答案 0 :(得分:3)

有很多方法可以做到这一点。一种简单的方法是将您的“排除的数字”保留在std::set中,并在每次生成随机数后,检查它是否在集合中,然后再生成一个新的随机数-重复此操作,直到获得一个不在集合中的数字,然后将其返回。

顺便说一句;虽然发行版的构建成本低廉,但引擎却并非如此。您不想每次调用该函数时都重新构建mt19937,而是一次创建它,然后重新使用它。您可能还想使用比当前时间(秒)更好的种子。

答案 1 :(得分:1)

您是否1)尝试在离散间隔内不进行替换而进行采样?还是2)区间内的补丁分布相当稳定?

如果是1),则可以按照此处的答案使用std :: shuffle How to sample without replacement using c++ uniform_int_distribution

如果是2),则可以使用std :: discrete_distribution(元素0对应于lwr_lm),并将不需要的数字加权为零。显然,upper_lm-lwr_lm中的内存需求是线性的,因此如果内存需求很大

答案 2 :(得分:1)

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

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

  2. Bloom filter。同样的想法,使用您愿意保留的每个缓存项的任意位来构建过滤器,并通过快速检查获得“可能在缓存中”的答案或清除否定的答案。您可以将答案精度换成内存,反之亦然。误报是可能的

答案 3 :(得分: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;
}

答案 4 :(得分:0)

正如@virgesmith所说,在回答问题时,这可能是更好的解决方案。
对于大范围wiki,带有缓存并用于过滤未来生成的方法效率不高。

在这里,我用不同的方法编写了一个简单的示例,但是您会受到记忆的限制。您选择一个随机数作为缓冲区,然后将其删除以进行下一次迭代。

#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;
} 

编辑:更清洁的解决方案here