我正在尝试使用容器(例如vector,set等)提取元素,但不是使用索引,而是使用位掩码技术。
vector<string> alphabets {"a", "b", "c", "d", "e"};
输入:5
(等效位掩码:00101
)
输出:新向量{"c", "e"}
输入13
(位掩码:01101
)
输出向量:{"b", "c", "e"}
vector<string*> extract(int mask){
vector<string*> result;
bitset<n> bits(mask);
for (int j = 0; j < n; ++j) {
if (bits[j]){
result.push_back(&alphabets[j]);
}
}
}
排列a,b,c,d,e的所有组合,其中a,b,c,d,e被包裹在容器上。 (Generating combinations in c++问题中已提及其他方法。)
#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <bitset>
using namespace std;
int main(){
const int n = 5;
vector<string> alphabets {"a", "b", "c", "d", "e"};
for ( int i = 0; i < pow(2, n); ++i){
vector<string*> result;
bitset<n> bits(i);
for (int j = 0; j < n; ++j) {
if (bits[j]){
result.push_back(&alphabets[j]);
}
}
for (auto r: result){
cout << *r;
}
cout << endl;
}
return 0;
}
答案 0 :(得分:2)
如果你赞成表现优于可读性,我认为这是一个合理的起点。
基本上我避免任何内存分配。
#include <string>
#include <vector>
#include <bitset>
#include <iostream>
#include <iterator>
#include <tuple>
#include <array>
template<class From, std::size_t N>
auto
select(From const& from, std::bitset<N> const& bits)
{
std::array<const std::string*, N> result { nullptr };
auto i = std::begin(result);
std::size_t found;
std::size_t count = found = bits.count();
std::size_t index = 0;
while (count)
{
if (bits.test(index)) {
*i++ = &from[index];
--count;
}
++index;
}
return std::make_tuple(found, result);
}
int main()
{
std::vector<std::string> alphabet = { "a", "b", "c", "d", "e", "f", "g", "h" };
for (unsigned x = 0 ; x < 256 ; ++x)
{
auto info = select(alphabet, std::bitset<8>(x));
auto ptrs = std::get<1>(info).data();
auto size = std::get<0>(info);
while(size--)
{
std::cout << *(*ptrs++) << ", ";
}
std::cout << '\n';
}
}
这里我在编译时预先计算所有可能的字母表。
运行时当然是快速的。但是,超过14个字符的字母表可能需要一段时间才能编译......
更新:警告!当我将字母大小设置为16 clang以消耗32GB内存时,停止桌面上的所有其他应用程序,并且在我可以执行任何其他操作之前需要重新启动我的macbook。你被警告了。
#include <string>
#include <vector>
#include <bitset>
#include <iostream>
#include <iterator>
#include <tuple>
#include <array>
template<class From, std::size_t N>
auto
select(From const& from, std::bitset<N> const& bits)
{
std::array<const std::string*, N> result { nullptr };
auto i = std::begin(result);
std::size_t found;
std::size_t count = found = bits.count();
std::size_t index = 0;
while (count)
{
if (bits.test(index)) {
*i++ = &from[index];
--count;
}
++index;
}
return std::make_tuple(found, result);
}
template<std::size_t Limit>
struct alphabet
{
constexpr alphabet(std::size_t mask)
: size(0)
, data { }
{
for (std::size_t i = 0 ; i < Limit ; ++i)
{
if (mask & (1 << i))
data[size++] = char('a' + i);
}
}
std::size_t size;
char data[Limit];
friend decltype(auto) operator<<(std::ostream& os, alphabet const& a)
{
auto sep = "";
for (std::size_t i = 0 ; i < a.size; ++i)
{
std::cout << sep << a.data[i];
sep = ", ";
}
return os;
}
};
template<std::size_t Limit>
constexpr alphabet<Limit> make_iteration(std::size_t mask)
{
alphabet<Limit> result { mask };
return result;
}
template<std::size_t Limit, std::size_t...Is>
constexpr auto make_iterations(std::index_sequence<Is...>)
{
constexpr auto result_space_size = sizeof...(Is);
std::array<alphabet<Limit>, result_space_size> result
{
make_iteration<Limit>(Is)...
};
return result;
}
template<std::size_t Limit>
constexpr auto make_iterations()
{
return make_iterations<Limit>(std::make_index_sequence<std::size_t(1 << Limit) - 1>());
}
int main()
{
static constexpr auto alphabets = make_iterations<8>();
for(const auto& alphabet : alphabets)
{
std::cout << alphabet << std::endl;
}
}
答案 1 :(得分:1)
那么,那么你的例子可以更短(更短到一点,它可能没有展示你真正需要的东西)(编辑:我计划将字符直接输出到cout
,完全不使用result
向量,然后我忘了它,当我重写代码时......所以它仍然与你的相似):
#include <vector>
#include <iostream>
#include <string>
int main() {
const std::vector<std::string> alphabets {"a", "b", "c", "d", "e"};
const unsigned N = alphabets.size();
const unsigned FULL_N_MASK = 1 << N;
for (unsigned mask = 0; mask < FULL_N_MASK; ++mask) {
std::vector<const std::string*> result;
unsigned index = 0, test_mask = 1;
while (index < N) {
if (mask & test_mask) result.push_back(&alphabets[index]);
++index, test_mask <<= 1;
}
for (auto r : result) std::cout << *r;
std::cout << std::endl;
}
return 0;
}
这让我有点奇怪,为什么你需要从掩码中提取result
向量,也许你只能使用mask
本身,并获取内部循环内的特定字符串(就像我在构建result
)时一样。
我的代码中的主要更改是省略bitset<n> bits(i);
初始化,因为您已经在i
中使用了本地位,可以使用C语言按位运算符(<< >> & ^ | ~
)轻松访问,其中&# 39;不需要再次使用bitset
将它们转换为相同的内容。
bitset
太大而无法使掩码适合某些常规类型(如n
),那么 uint32_t
用法就有意义了。即便如此,对于相当小的固定n
,我可能会使用很少的64/128 / 256b无符号整数(目标平台上可用的内容)。
关于速度:当然,您无法击败++mask
。 std::next_permutation
将比单个本地机器代码指令慢,即使它以32/64以下的大小以相同的方式实现。
但问题是,如果您可以围绕该位掩码构建算法,则可以有效地利用该优势。
许多国际象棋引擎使用各种棋盘值的位掩码编码,轻松检查某些领域是否被占用,或者在一个步骤中进行一些初步的转弯可用性检查,例如让所有可以在下一轮获得对手数字的棋子:{ {1}} - 5个本机CPU按位运算符,如果结果为0,则表示您的棋子无法攻击任何一个东西。或者你有可以采取的对手棋子的位掩码。将其与所有作品的幼稚循环进行比较,并检查对手棋子的[+ -1,+ 1]字段,并将其添加到attacking_pawns = (board_opponent & (my_pawns<<9) & valid_left_forward_pawn_take_mask) | (board_opponent & (my_pawns<<7) & valid_right_forward_pawn_take_mask); // for the side going "up" on board
等动态分配的内存中。
或者是否存在能够通过提前退出来修剪组合树的算法,完全跳过一些实质性的排列子集,这可能比完全vector
扫描更快。