C++ boost::dynamic_位集的相反顺序

C++ boost::dynamic_位集的相反顺序,c++,boost,reverse,boost-dynamic-bitset,C++,Boost,Reverse,Boost Dynamic Bitset,是否有一种干净的方法可以返回boost::dynamic_位集对象的反向顺序 例如: 01001100 变成 00110010。 我能想到的最简单的解决方案是将位集转换为字符串,反转字符串并将其转换回位集,但这似乎是一个相当慢的方法,它会使位字符串操作的速度为零 提前谢谢你 boost::dynamic_bitset没有迭代器,因此一系列舒适的STL解决方案,例如,在我的脑海中,std::reverse或std::swap或它们的boost对应项都不可用,我认为一个好方法是制作自己的简单的反向方

是否有一种干净的方法可以返回
boost::dynamic_位集
对象的反向顺序

例如:
01001100
变成
00110010
。 我能想到的最简单的解决方案是将位集转换为字符串,反转字符串并将其转换回位集,但这似乎是一个相当慢的方法,它会使位字符串操作的速度为零


提前谢谢你

boost::dynamic_bitset
没有迭代器,因此一系列舒适的STL解决方案,例如,在我的脑海中,
std::reverse
std::swap
或它们的
boost
对应项都不可用,我认为一个好方法是制作自己的简单的反向方法:

#include <iostream>
#include <boost/dynamic_bitset.hpp>

void reverse(boost::dynamic_bitset<> &bs)
{
    for (size_t begin = 0, end = bs.size() - 1; begin < end; begin++, end--)
    {
        bool b = bs[end];
        bs[end] = bs[begin];
        bs[begin] = b;
    }
}

int main()
{
    size_t size = 8;
    boost::dynamic_bitset<> bs(size, 50);

    std::cout << "Normal:  " << bs << std::endl;
    reverse(bs);
    std::cout << "Reverse: " << bs << std::endl;
}

有了非常幸运的人,你可以做得更好

 #define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS
我首先注意到它,因为它在其他第三方库中使用(我忘了它的名字,但它与AI/ML有关)

我这里有一个版本不是很通用,因为它使用了特定于大小的位旋转技术(例如,反转字节或uint32)。您可能会对以下内容感兴趣:

您仍然可以看到uint32专用版本

通用版本 从那以后,我发现了这个很好的答案:它为2次方宽度积分类型提供了一个相当有效的就地反向算法。因此,现在我们有了完全通用的:

// make sure it's globally defined
#define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS
#include <boost/dynamic_bitset.hpp>
#include <iostream>

template <typename Block, typename Allocator>
void reverse(boost::dynamic_bitset<Block, Allocator>& bs) {
    auto constexpr BLOCK_BIT = sizeof(Block) * CHAR_BIT;
    auto original_size       = bs.size();

    if (auto partial_block = bs.size() % BLOCK_BIT) {
        auto pad = (BLOCK_BIT - partial_block);
        bs.resize(bs.size() + pad);
        bs <<= pad;
    }

    // see https://stackoverflow.com/a/61109975/85371
    auto inplace = [](Block& n) {
        static_assert(std::is_unsigned_v<Block>);

        short bits = sizeof(n) * 8;
        Block mask = ~Block(0); // equivalent to uint32_t mask =
                                // 0b11111111111111111111111111111111;

        while (bits >>= 1) {
            mask ^= mask << (bits); // will convert mask to
                                    // 0b00000000000000001111111111111111;
            n = (n & ~mask) >> bits | (n & mask) << bits; // divide and conquer
        }
    };

    for (auto& b : bs.m_bits) {
        inplace(b);
    }
    std::reverse(begin(bs.m_bits), end(bs.m_bits));

    bs.resize(original_size);
}
打印,在我的机器上:

Completed 10'000 tests with char blocks
Completed 10'000 tests with uint16_t blocks
Completed 100'000 tests with uint32_t blocks
Completed 1'000'000 tests with uintmax_t blocks
Samples 1120000 mean: 90.3283 min: 0 max: 192 stddev: 55.9233
Average cost 3.69335μs

real    0m4,141s
user    0m4,061s
sys     0m0,003s
因此,在平均大小为90位的情况下,最多192位的位设置可以在不到4μs的时间内反转。不错

适当的微观基准 通过使用,我们可以从可预测的测试中获得可靠的数据。对于大小为31、32、37位的网络计时在10-30ns范围内

使用的代码:

#define NONIUS_RUNNER
#include <nonius/nonius.h++>
#include <nonius/main.h++>

template <typename Block> void run_test(nonius::chronometer& cm, size_t target_size) {
    using BitSet = boost::dynamic_bitset<Block>;

    static const std::string data{
        "0100110111010010010001100111010010010001011100100100111010100010011010"
        "01100000011000010001110111"};

    BitSet bs(data, 0, target_size);
    assert(bs.size() == target_size);

    cm.measure([&] { reverse(bs); });
}

NONIUS_BENCHMARK("Block=uchar,     sz=32", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   32); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=32", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  32); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=32", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  32); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=32", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 32); })

NONIUS_BENCHMARK("Block=uchar,     sz=31", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   31); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=31", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  31); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=31", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  31); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=31", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 31); })

NONIUS_BENCHMARK("Block=uchar,     sz=37", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   37); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=37", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  37); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=37", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  37); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=37", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 37); })
#定义NONIUS\u RUNNER
#包括
#包括
模板无效运行测试(nonius::天文计和厘米,尺寸和目标尺寸){
使用BitSet=boost::dynamic_位集;
静态常量std::字符串数据{
"0100110111010010010001100111010010010001011100100100111010100010011010"
"01100000011000010001110111"};
位集bs(数据,0,目标大小);
断言(bs.size()==目标大小);
cm.度量([&]{reverse(bs);});
}
NONIUS_基准(“Block=uchar,sz=32”,[](NONIUS::天文计cm){运行_测试(cm,32);})
NONIUS_基准(“Block=uint16_t,sz=32”,[](NONIUS::天文计cm){运行_测试(cm,32);})
NONIUS_基准(“Block=uint32_t,sz=32”,[](NONIUS::天文计cm){运行_测试(cm,32);})
NONIUS_基准(“Block=uintmax_t,sz=32”,[](NONIUS::天文计cm){运行_测试(cm,32);})
NONIUS_基准(“Block=uchar,sz=31”,[](NONIUS::天文计cm){运行_测试(cm,31);})
NONIUS_基准(“Block=uint16_t,sz=31”,[](NONIUS::天文计cm){运行_测试(cm,31);})
NONIUS_基准(“Block=uint32_t,sz=31”,[](NONIUS::天文计cm){运行_测试(cm,31);})
NONIUS_基准(“Block=uintmax_t,sz=31”,[](NONIUS::天文计cm){运行_测试(cm,31);})
NONIUS_基准(“Block=uchar,sz=37”,[](NONIUS::chronometer cm){运行_测试(cm,37);})
NONIUS_基准(“Block=uint16_t,sz=37”,[](NONIUS::天文计cm){运行_测试(cm,37);})
NONIUS_基准(“Block=uint32_t,sz=37”,[](NONIUS::天文计cm){运行_测试(cm,37);})
NONIUS_基准(“Block=uintmax_t,sz=37”,[](NONIUS::天文计cm){运行_测试(cm,37);})
完整互动图表:


看来
boost::dynamic_bitset
的“块”部分只完成了一半。在中间块之前不能循环块,所以逐块交换是不可能的。我认为我们可以做得更好。让我check@sehe如果可能的话,你肯定是这份工作的合适人选。为了以防万一,我已经把最好的方式改成了好的方式;)(干杯:)好的,我在我的机器上。。样本1120000平均值:90.3283最小值:0最大值:192标准偏差:55.9233平均成本3.69335μs/cc@m替换整个区块的长度?这允许“反向复制到临时,反向每个块,复制回”的实现。“基准测试”补充道:“你确实做到了,非常好。”阿纳斯塔乔我刚刚意识到我的计时反映了测试结果。它们使用字符串转换和反转作为检查:),所以它们非常无用。哦,好吧,细节;)@anastaciu本身:10-30ns范围内的实际计时。确认对齐输入上的32位收藏夹。用于次优尺寸的intmax\t领带。完整互动图表:
Completed 10'000 tests with char blocks
Completed 10'000 tests with uint16_t blocks
Completed 100'000 tests with uint32_t blocks
Completed 1'000'000 tests with uintmax_t blocks
Samples 1120000 mean: 90.3283 min: 0 max: 192 stddev: 55.9233
Average cost 3.69335μs

real    0m4,141s
user    0m4,061s
sys     0m0,003s
#define NONIUS_RUNNER
#include <nonius/nonius.h++>
#include <nonius/main.h++>

template <typename Block> void run_test(nonius::chronometer& cm, size_t target_size) {
    using BitSet = boost::dynamic_bitset<Block>;

    static const std::string data{
        "0100110111010010010001100111010010010001011100100100111010100010011010"
        "01100000011000010001110111"};

    BitSet bs(data, 0, target_size);
    assert(bs.size() == target_size);

    cm.measure([&] { reverse(bs); });
}

NONIUS_BENCHMARK("Block=uchar,     sz=32", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   32); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=32", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  32); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=32", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  32); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=32", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 32); })

NONIUS_BENCHMARK("Block=uchar,     sz=31", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   31); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=31", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  31); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=31", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  31); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=31", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 31); })

NONIUS_BENCHMARK("Block=uchar,     sz=37", [](nonius::chronometer cm) { run_test<uint8_t>(cm,   37); })
NONIUS_BENCHMARK("Block=uint16_t,  sz=37", [](nonius::chronometer cm) { run_test<uint16_t>(cm,  37); })
NONIUS_BENCHMARK("Block=uint32_t,  sz=37", [](nonius::chronometer cm) { run_test<uint32_t>(cm,  37); })
NONIUS_BENCHMARK("Block=uintmax_t, sz=37", [](nonius::chronometer cm) { run_test<uintmax_t>(cm, 37); })