我正在玩弄创建一个小的跳棋解算器的想法。首先,我制作一个非常紧凑的棋盘表示,然后继续构建游戏树等。
标准检查板有 8行, 4个功能列(检查员只能移动对角线)。这给了我们32个职位!每个位置需要3位信息... king
位和color
位...所以00
非黑王,{{1} } 非王红,01
是王黑,10
是王红。这给了我们64这是一个很好的数字(长整数的精确大小)。
但是,每个检查器还需要一个额外的位... 11
位,因为每个检查器位置可以为空,或者填充上述四种状态之一。我决定采用64个状态并将它们放入一个长64位int,并将32个占用状态并将它们放入一个32位整数。
所以现在我们有了一些背景知识,我有以下问题:我想轻松地说#34;这块板上有多少个红色检查器?"嗯,这不是那么糟糕......我们的64位整数保存这样的数据:
isOccupied
所以king_color_king_color_king_color
意味着我们有红色,黑色的国王,红色。
为了得到颜色信息,我们可以使用01010101 ... 01的位掩码,它是十六进制的0x5555555555555555。这会将国王的位置归零,只留下颜色位。因此,在使用掩码进行AND运算后的011001示例中,我们有010001.如果我们计算位(011001
,popcount
),我们得到红色数...
bitcount
代表3个检查员职位......一个空的检查员之前是红色,接着是黑色国王,接着是红色。一旦我们在64位上执行010101掩码操作,我们就得到:
32bit = 011
64bit = 011001
天真地我们有2个红色...但我们实际上只有1个活动...我想要做的主要是采用64位字符串中的奇数位,并将它们与32位字符串中的每个位进行AND运算......即
64bitWithMask = 010001
32bit=011
给我们001代表红色棋子的数量。
或等效地,将1 AND 0, 0 AND 1, 1 AND 1
转换为64bitWithMask
然后简单地用32位AND来获得64bitWithMaskOddBits = 101
。
那么形式上,有没有办法取一个长度为2X的字符串,并通过只取奇数位将其减少到长度X?我正在努力避免循环,ifs等,并且只使用逻辑(和,或,xor,否定等)。
当然,如果有另一种策略可以在我的32位和64位字符串中获得正确的红色数。 谢谢!
我提出的问题的解决方案在接受的答案中优雅地解决了,但是对于我的实际应用程序更好的解决方案是将64位表示分成两个32。这节省了我一堆操作来提取我需要的东西。感谢LukeG和Tehtmi的帮助!我很高兴接触到这种新的位操作技术," parallel"。
答案 0 :(得分:5)
将每个其他位从一个数字压缩成一个半长数字有点棘手,因为每个位需要移位一个不同的数量。但是,有一种聪明的方法可以比单独处理每个位需要更少的操作。对于64位,它看起来像这样(伪代码):
x = x & 0x5555555555555555
// or for the alternate bits: x = (x >> 1) & 0x5555555555555555
x = (x | (x >> 1)) & 0x3333333333333333
x = (x | (x >> 2)) & 0x0f0f0f0f0f0f0f0f
x = (x | (x >> 4)) & 0x00ff00ff00ff00ff
x = (x | (x >> 8)) & 0x0000ffff0000ffff
x = (x | (x >> 16)) & 0x00000000ffffffff
这里说明了32位数字(在初始掩码之后)每一步的位发生了什么:
0a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p
00ab00cd00ef00gh00ij00kl00mn00op
0000abcd0000efgh0000ijkl0000mnop
00000000abcdefgh00000000ijklmnop
0000000000000000abcdefghijklmnop
例如,位g
需要向右移动9
,因此请查看两个幂组件9 = 1 + 8
。因此,g
步骤和>> 1
步骤中>> 8
移位。
这种比特算法有时被描述为" parallel"。您可能有兴趣查看此famous list。 (它包括与这里发生的事情密切相关的交错。)
此类代码的标准免责声明通常难以使用,因此除非实际存在性能问题,否则它可能不应该用于严肃的项目中(即使这样,也要确保它清楚是什么代码应该做,例如使用注释)。如果没有性能问题并且您仍然希望使用位操作,那么循环解决方案可能仍然是首选,因为它更容易理解和使用。
答案 1 :(得分:3)
如果不使用循环,我认为没有办法做到这一点。
编辑:我被tehtmi证明是错的。 Wile我仍然认为在这个答案的最后提出的“替代解决方案”是解决手头问题的更好方法,tehtmi提出了一个非常有趣的解决方案,如果你还没有,你应该向上滚动并提升它。 / p>
我看到两种方法来解决这个问题。
第一个接近你想要实现的目标是:
uint32_t occupied;
uint64_t data;
uint32_t occupiedWithRed;
for (auto i = 0; i < 32; ++i) {
occupiedWithRed |= (data >> i) & occupied & (1 << i);
}
红色位置的计数将是occupiedWithRed中设置位的计数。
更容易(也可能更快)的方式是:
uint32_t occupied;
uint64_t data;
auto count = 0;
for (auto i = 0; i < 32; ++i) {
if ((data >> (2 * i)) & (occupied > i)) ++count;
}
或者,做一些完全不同的事情:正如评论中所提到的,如果您将数据分成3个不同的32位无符号整数,则可以减轻您的生活。一个用于区分红色和黑色,一个用于区分自由和占用,一个用于区分国王和国王。通过这种方式,您的任务将变得非常容易这将是一个按位并且计算汉明重量的问题。
答案 2 :(得分:0)
不是收集偶数或奇数位以与32个占用位进行比较,而是采用另一种方式将它们分配到64位整数,使得它们仅在奇数位置上进行。另外,我将它们移到另一个64位整数的偶数位置。
然后,您可以轻松地将奇数位置或偶数位置的占用整数与位置信息整数中的偶数或奇数位进行比较。