C++ 从紧凑整数数组中选择随机元素

C++ 从紧凑整数数组中选择随机元素,c++,algorithm,bit-manipulation,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 ui

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

/*
 * 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;
  }
};
/*
*紧凑地表示N个无符号整数的数组,其中每个
*需要B位来存储。
*/
模板
类_属性_((压缩))小数组{
私人:
每页静态成本32项=64/B;
静态constuint32\u t num\u pages=(N+每页项目数-1)/每页项目数;
静态常数64屏蔽单元=(1UL移位;
}
无效集(uint32索引,uint8值){
助手(索引);;
uint8\u t shift=B*helper.offset;

_pages[helper.page]&=~0UL-(mask_unit我没有看到很多避免内部循环的选项。这对我来说非常基本:你必须检查“page”中的每个值是否与参数值匹配。需要一个循环。对我来说似乎非常基本

我认为避免显式循环的唯一方法是编写一个毛茸茸的专用函数,该函数本质上为页面中的每个值生成显式编译时检查。这是可能的,因为您已经计算出每页的
。将其输入
std::index\u序列
以获取所有索引,并传递m是一个可变函数,手动将“页面”中的每个项目与
值进行比较


从技术上讲,这将避免显式的内部循环,但我怀疑它是否会有很大的不同。

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

我认为避免显式循环的唯一方法是编写一个毛茸茸的专用函数,该函数本质上为页面中的每个值生成显式编译时检查。这是可能的,因为您已经计算出每页的
。将其输入
std::index\u序列
以获取所有索引,并传递m是一个可变函数,手动将“页面”中的每个项目与
值进行比较


从技术上讲,这将避免显式的内部循环,但我怀疑它会有很大的不同。

谢谢。如果我们修正N=64、B=1和value=0,你看到任何可能的改进吗?如果
N
是64,而
B
是1,那么你可以忘记整个事情,因为你所拥有的是不幸的
std::vector
till坚持自己滚动。谢谢。我的实际使用涉及N和B的不同值,所以我需要我的实现。我只是想知道是否有更好的
get\u random\u index()实现
对于一个简单的案例。如果我们连简单的案例都不能改进,那么对于一般的案例可能没有什么可以做的。谢谢。如果我们修正N=64,B=1,值=0,你看到有什么可能的改进吗?如果
N
是64,而
B
是1,那么你可以忘记整个事情,因为你所拥有的是不幸
std::vector
。如果您仍然坚持自己滚动。谢谢。我的实际使用涉及不同的N和B值,因此我需要我的实现。我只是想知道是否有更好的
get\u random\u index()实现
对于一个简单的情况。如果我们连简单的情况都不能改进,那么对于一般的情况可能没有什么可以做的。这两个嵌套循环不是等同于枚举索引0..max的函数
get
吗?如果是的话,那么只使用一个循环,在get-get结果匹配时记录候选函数如何是否删除传递的值?@danh是的,这是等效的,并且可能会以牺牲更多算术运算为代价来获得更清晰的代码。这样得到的代码看起来非常紧凑,特别是如果您将其内联到循环中。您可以内联helper()这两个嵌套循环是否等同于枚举索引0..max的函数
get
?如果是这样,那么只使用一个循环就可以做到这一点,当get-get结果与传递的值匹配时记录候选值?@danh是的,这是等效的,可能会使代码更清晰以牺牲更多的算术运算为代价。get代码看起来非常紧凑,尤其是在循环中内联时。您也可以内联helper()代码,或许可以节省一些额外的开销。
  /*
   * 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];
  }