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)。您可能会对以下内容感兴趣:
- 看
- 或
// 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); })