从紧凑的int数组中选择一个随机元素

时间:2016-07-11 02:29:01

标签: c++ algorithm bit-manipulation

我创建了一个数据结构来紧凑地表示一个小整数数组:

/*
 * Compactly represents an array of N unsigned integers, where each one only
 * requires B bits to store.
 */
template<uint32_t N, uint8_t B>
class __attribute__((packed)) small_int_array {
private:
  static const uint32_t items_per_page = 64 / B;
  static const uint32_t num_pages = (N + items_per_page - 1) / items_per_page;
  static const uint64_t mask_unit = (1UL << B) - 1;

  struct helper_t {
    uint32_t page;
    uint8_t offset;

    helper_t(uint32_t index) : page(index/items_per_page),
      offset(index%items_per_page) {}
  };

  uint64_t _pages[num_pages];

public:
  small_int_array() { memset(this, 0, sizeof(this)); }

  uint8_t get(uint32_t index) const {
    helper_t helper(index);
    uint8_t shift = B*helper.offset;
    return (_pages[helper.page] & (mask_unit << shift)) >> shift;
  }

  void set(uint32_t index, uint8_t value) {
    helper_t helper(index);
    uint8_t shift = B*helper.offset;
    _pages[helper.page] &= ~0UL - (mask_unit << shift);
    _pages[helper.page] |= ((uint64_t)value) << shift;
  }
};

然后我实现了这个特殊的方法:

  /*
   * Returns a uniformly random index such that get(index)==value.
   * Returns -1 if no such index exists.
   */
  int32_t get_random_index(uint8_t value) const {
    int32_t candidates[N];
    int size=0;
    uint32_t index = 0;
    for (int i=0; i<num_pages; ++i) {
      uint64_t page = _pages[i];
      for (int j=0; j<items_per_page; ++j) {
        candidates[size] = index++;
        if (index==N) break;
        bool match = (page & mask_unit) == value;
        size += match ? 1 : 0;
        page = page >> B;
      }
    }
    if (size==0) return -1;
    return candidates[rand() % size];
  }

这就是我想要的,但我想知道是否有更高效的实现使用位技巧代替第二个for-loop。只要它的大小没有增加,我就可以改变对象的位表示。

我使用N~ = 100且B <= 4。

1 个答案:

答案 0 :(得分:0)

我没有看到很多避免内循环的选项。这对我来说非常重要:你必须检查“页面”中的每个值是否与参数值匹配。需要一个循环。对我来说似乎很重要。

我看到避免显式循环的唯一方法是编写一个毛茸茸的专用函数,实质上,它为页面中的每个值生成一个显式的编译时检查。这是可能的,因为你已经解决了items_per_page。将其输入std::index_sequence以获取所有索引,并将其传递给可变参数函数,该函数会手动将“页面”中的每个项目与value进行比较。

从技术上讲,这将避免明确的内循环,但我怀疑它会产生很大的不同。