考える:選択スパン
- 少なくとも一組(
1
)ビットを含むビットマスクa
(例えば、std::uint64_t
)。 a
(すなわちa & b == b
)のサブセットであり、少なくとも1ビットが設定されたセレクタビットマスクb
。
Iはb
のビットと重複a
で連続1ビットのスパンを選択する:
a = 0b1111001110001100;
b = 0b0000001010001000;
//c=0b0000001110001100
// XXXX YYY ZZ
b & XXXX
が偽であるため、XXXX基はc
に0です。 b
にはZビットの1つが設定されているため、ZZグループがコピーされます。 YYYグループも同じ理由でc
に設定されています。 b
は、単一グループの複数のビットをa
にすることができます。 b
は、これらの位置のいずれかに1
を有する場合
そこでa
で1
Sのすべての連続したグループのために、c
にこれらのビットのすべてを設定します。より複雑な例:
std::uint64_t a = 0b1101110110101;
std::uint64_t b = 0b0001010010001;
// desired c == 0b0001110110001
// contiguous groups ^^^ ^^ ^that overlap with a 1 in b
assert(a & b == b); // b is a subset of a
std::uint64_t c = some_magic_operation(a, b);
assert(c == 0b0001110110001);
私はa
かつ効率的にb
からc
を計算することができます任意のビット論理命令/組み込み関数(MMX、SSE、AVX、BMI1/BMI2)、またはビット操作のトリックはありますか? (ループなし)?
追加:
てみましょう:あなたはこのトリックを行うことuint64_t
の場合
std::uint64_t a = 0b0110111001001101;
std::uint64_t b = 0b0100101000001101;
assert(a & b == b); // subset
std::cout << std::bitset<16>(a) << std::endl;
std::cout << std::bitset<16>(b) << std::endl;
std::uint64_t x = (a + b) & ~a;
std::uint64_t c = 0;
while ((x = (a & (x >> 1)))) { // length of longest 1-series times
c |= x;
}
std::cout << std::bitset<16>(c) << std::endl;
は、「セグメント」は、長さ1のセグメントが含まれていますか? –
@ M.Mはい、1セグメントは長さが1ではない長さゼロのシーケンスです。 – Orient
あなたの説明を解読するのに約10分かかったので、将来の読者のために明確にするために編集しました。私はあなたがインテルのBMI1/BMI2エクステンションを誤って削除したと考えています。なぜなら、[Haswellはあまりにも新しい](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets#Supporting_CPUs)ではないからです。あなたはAVXについて言及しましたが、これは決してベースラインではありません。[Skylake Celeron/Pentium](http://ark.intel.com/products/90732)でもサポートされていますが、i3以降のみサポートされています。ありがとう、インテル:( –