【问题标题】:Create new map from random subset of map in C++在 C++ 中从地图的随机子集创建新地图
【发布时间】:2016-12-19 14:11:04
【问题描述】:

我有一张地图std::map<unsigned int,double> areas,我想从中绘制一个随机大小的随机子集(均匀分布)。到目前为止,我知道如何选择一个随机元素。

const int range_from  = 1;
const int range_to    = areas.size();
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);
std::map<unsigned int,double>::iterator rand_element;
auto itrtr = ++areas.begin();
std::advance(itrtr, distr(generator));
unsigned int random_key = itrtr->first;
rand_element = areas.find(random_key);

我将如何扩展此算法?或者不同的ansatz会更好吗?

【问题讨论】:

  • @m.s.使用随机排列是一个非常优雅的解决方案
  • 目标大小真的是随机的,还是您有准确的样本数?
  • Mark Ransom - 是的。但是知道如何指定子集的大小当然很好。到目前为止,我只能找到例如insert method。但我想知道是否还有一种方法我只需要指定子集的长度而不是位置。但由于地图是预先随机化的,我可以简单地使用插入方法并将地图从其开头子集到任意(

标签: c++


【解决方案1】:

借助m.s. 的评论和Johnny Mnemonicthis question 的回答,我可以想出这个解决方案:

std::map<unsigned int,double> areas;
areas = computeAreas(); //fill areas map
areas[0] = 0;
std::vector<unsigned int> v;
// copy map keys to vector
  for(auto it = ++areas.begin(); 
    it != areas.end(); ++it) {
    v.push_back(it->first);
  }
// randomize vector elements
std::random_shuffle(v.begin(), v.end());
auto it=v.begin();

  for(auto& i:areas){
    double values=i.second;
    i.second=areas[*it];
    areas[*it]=values;
    it++;
  }

【讨论】:

  • std::random_shuffle 已弃用,很快将被删除。更多autos 也会有很大帮助。
  • Quentin - 谢谢,我更正了代码。关于std::random_shuffle 的替代品,我希望还有一些时间可以找到。 :)
【解决方案2】:

要获得随机抽样,您可以遍历整个地图,使用随机数来确定哪些元素将被保留,哪些元素将被丢弃。有两种方法可以计算保留与丢弃的几率,具体取决于您是否需要精确计数。

对于随机计数,使用例如平均抽取 10 个样本中的 1 个:

int sample_rate = 10;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(0, sample_rate - 1);
std::map<unsigned int,double> result;
for (auto it = areas.begin(); it != areas.end(); ++it)
{
    if (distr(generator) == 0)
        result.insert(*it);
}

对于准确的计数,例如输入的 1/10:

int result_size = areas.size() / 10;
int countdown = areas.size() - 1;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::map<unsigned int,double> result;
for (auto it = areas.begin(); it != areas.end(); ++it, --countdown)
{
    std::uniform_int_distribution<int> distr(0, countdown);
    if (distr(generator) < result_size - result.size())
        result.insert(*it);
}

【讨论】:

  • 谢谢Mark,你的代码教会了我一点。这仅适用于相当大的areas.size()。就我而言,它会随着时间而变化,即从 1e0 到 1e3。因此,我假设我要么遵循另一个 ansatz,要么在 result_size 的右侧添加一个补偿性术语。
  • @fitzy_blarb 当然,您可以使用任何您想计算result_size 的公式,或者将其从其他地方传递给函数。即使result_size 大于您的原始数据集,该代码仍然有效,在这种情况下返回整个数据集。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 1970-01-01
  • 2019-05-20
  • 2019-08-18
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
相关资源
最近更新 更多