请考虑以下centered hexagonal位板表示(填充以粗体显示):
56
55 49
54 48 42
53 47 41 35
52 46 40 34 28
45 39 33 27
44 38 32 26 20
37 31 25 19
36 30 24 18 12
29 23 17 11
28 22 16 10 04
21 15 09 03
20 14 08 02 60
13 07 01 59
06 00 58
63 57
56
此表示形式适合64位整数,并且通过分别向右或向左旋转位1、7或8位,可以在6个六边形方向上轻松移动。如果有助于可视化,则可以将此六角形变形为正方形:
42 43 44 45 46 47 48
35 36 37 38 39 40 41
28 29 30 31 32 33 34
21 22 23 24 25 26 27
14 15 16 17 18 19 20
07 08 09 10 11 12 13
00 01 02 03 04 05 06
现在,我要做的是将该位板顺时针旋转60°,以使[45,46,47,38,39,31]三角形变为[48,41,34,40,33,32]三角形等。我该怎么做?
答案 0 :(得分:3)
此排列有点混乱,每个相关位都有不同的移动距离。排列图如下所示(输出第一行):
这确实暗示了一些方法。如果我们靠近顶部,则每个“组”都是通过从输入中按升序收集一些位而形成的,因此可以通过7 compress_right个操作(也称为PEXT
)来完成,这在Intel上很有效(不是到目前为止在AMD上如此高效)。真正的结果是对垂直列进行采样,因此提取步幅为8的位。
因此,如果PEXT
是可以接受的,则可以这样做(未经测试):
uint64_t g0 = _pext_u64(in, 0x8080808);
uint64_t g1 = _pext_u64(in, 0x404040404);
uint64_t g2 = _pext_u64(in, 0x20202020202);
uint64_t g3 = _pext_u64(in, 0x1010101010101);
uint64_t g4 = _pext_u64(in, 0x808080808080);
uint64_t g5 = _pext_u64(in, 0x404040404000);
uint64_t g6 = _pext_u64(in, 0x202020200000);
uint64_t out = g0 | (g1 << 7) | (g2 << 14) | (g3 << 21) |
(g4 << 28) | (g5 << 35) | (g6 << 42);
此排列无法通过蝶形网络进行路由,但是Beneš网络是通用的,因此可以正常工作。
因此,可以使用these个置换步骤中的11个来完成,也称为增量交换:
word bit_permute_step(word source, word mask, int shift) {
word t;
t = ((source >> shift) ^ source) & mask;
return (source ^ t) ^ (t << shift);
}
如何创建精确的蒙版有一些选择,但这可行:
x = bit_permute_step(x, 0x1001400550054005, 1);
x = bit_permute_step(x, 0x2213223111023221, 2);
x = bit_permute_step(x, 0x01010B020104090E, 4);
x = bit_permute_step(x, 0x002900C400A7007B, 8);
x = bit_permute_step(x, 0x00000A0400002691, 16);
x = bit_permute_step(x, 0x0000000040203CAD, 32);
x = bit_permute_step(x, 0x0000530800001CE0, 16);
x = bit_permute_step(x, 0x000C001400250009, 8);
x = bit_permute_step(x, 0x0C00010403080104, 4);
x = bit_permute_step(x, 0x2012000011100100, 2);
x = bit_permute_step(x, 0x0141040000000010, 1);