【问题标题】:C++ Randomly select non-empty element of std::vector<std::vector> >C++随机选择std::vector的非空元素<std::vector>>
【发布时间】:2017-12-20 12:07:35
【问题描述】:

我得到一个大向量,它本身包含特定数据类型的向量,例如std::vector&lt;std::vector&lt;double&gt; &gt; foo。我正在尝试从 foo 中检索一个随机元素 foo[idx] 以使 foo[idx] 非空或分别为 foo[idx].empty() == false

我的天真猜测是从foo 中选择随机元素,直到满足我的约束foo[idx].empty() == false

但是,foo 很可能非常稀疏地填充了非空向量。因此,我的方法很可能会非常缓慢。

是否有更好的方法或者我应该考虑完全不同的数据结构?

【问题讨论】:

  • std::shuffle 然后迭代直到找到一个。
  • @iBug std::shuffle 不会帮助程序压缩它。
  • @JakeFreeman 创建一个索引数组然后打乱索引也很好。
  • 随机的方法很好,特别是如果你可以缓存你已经尝试过的索引,这样你就不会再尝试了。

标签: c++ vector random


【解决方案1】:

用非空元素的索引维护一个辅助向量,并从中获取一个随机元素

【讨论】:

  • 这个想法效率不高,因为你要存储 2x 内存,并且需要时间来不断确保它们相同
  • 使用哈希表需要固定的时间来维护。关于记忆,是的,它需要记忆。但内存是免费的:D
【解决方案2】:

你可以建立一个非空元素的索引:

std::vector<std::vector<double> > foo;
std::vector<decltype (foo)::iterator> nonempty;
for (auto it = foo.begin(); it != foo.end; ++it)
{
  if (! it->empty())
  {
    nonempty.push_back(it);
  }
}
std::random_device rd;
// random-number engine used (Mersenne-Twister in this case) 
std::mt19937 rng(rd());
// create a guaranteed unbiased index (unlike using modulo on rand)
std::uniform_int_distribution<size_t> uni_idx_dist(0,nonempty.size() - 1); 

auto &random_nonempty = *nonempty[uni_idx_dist(rng)];

【讨论】:

    【解决方案3】:

    您可以先提取非空的索引,然后选择一个:

    std::vector<int> ind;
    for (int i = 0; i < foo.size(); i++){
        if (! foo[i].empty()) {
            ind.push_back(i);
        }
    }
    int i = rand() % int.size();
    return int[i];
    

    【讨论】:

      【解决方案4】:

      您可以构建一个引用非空向量的向量。

      #include <algorithm>
      #include <functional>
      #include <iterator>
      #include <random>
      #include <vector>
      #include <iostream>
      
      int main() {
          using int_vec_t = std::vector<int>;
          std::vector<int_vec_t> v = {
              {0, 1, 2}, {}, {}, {3, 4, 5},
              {}, {6, 7, 8}, {}, {}, {9}, {10, 11}
          };
      
          // You can't put reference direcly, so use reference_wrapper instead
          std::vector<std::reference_wrapper<int_vec_t> > nonempty;
          nonempty.reserve(v.size());
          // "copy" non empty vectors. (Doesn't do copy, actually)
          std::copy_if(v.begin(), v.end(), std::back_inserter(nonempty), [](const int_vec_t& v) { return !v.empty();});
          if (nonempty.empty())
              return 0;
          // pick an element
          static std::random_device rd;
          static std::mt19937 gen(rd());
          std::uniform_int_distribution<> dis(0, nonempty.size() - 1);
          const int_vec_t& result = nonempty[dis(gen)];
          // dump result
          std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, ", "));
      
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        这里有几个问题:

        1. 序数 vector 人口稀少
        2. A vector of vectors is wateful

        要解决问题1我建议使用map&lt;size_t, vector&lt;double&gt;&gt; foo 这将允许您使用非线性索引,但它不需要干预空@987654326 的人口@s。在这里选择一个随机填充的元素只涉及推进迭代器以指向适当的元素。例如result 将是foo 中随机键值pair 的常量迭代器:

        const auto idx = foo.empty() ? 0U : std::mt19937{std::random_device{}()}() % size(foo);
        const auto result = next(cbegin(foo), idx);
        

        12 的解决方案会更复杂一些,因为我建议一起取消 vectors 以支持 multimap&lt;size_t, double&gt; foo这包含了map 解决方案的所有优点,但代价是必须使用upper_bound 迭代密钥。此外,由于multimap 不存储密钥计数,因此size_t keyCount 需要与multimap 一起维护。或者假设它是一个临时初始化为0U,它可能会在需要的时候被浪费掉:for(auto it = cbegin(foo); it != cend(foo); it = foo.upper_bound(it-&gt;first)) ++keyCount;使用keyCount我们可以再次找到result,这将是第一个的常量迭代器匹配随机键的元素:

        int idx = keyCount == 0U ? 0 : std::mt19937{std::random_device{}()}() % keyCount;
        auto result = cbegin(foo);
        
        while(idx-- > 0) result = foo.upper_bound(result->first);
        

        【讨论】:

          猜你喜欢
          • 2011-12-27
          • 1970-01-01
          • 1970-01-01
          • 2016-03-30
          • 1970-01-01
          • 1970-01-01
          • 2013-08-17
          • 2012-03-07
          • 2016-04-06
          相关资源
          最近更新 更多