【发布时间】:2018-01-05 03:10:03
【问题描述】:
我正在玩弄创建一个小跳棋求解器的想法。首先我会制作一个非常紧凑的棋盘表示,然后继续构建游戏树等。
一个标准的棋盘有8行和4个功能列(棋子只能对角移动)。这给了我们 32 个职位!每个位置需要 3 位信息...king 位和color 位...所以00 是非国王黑色,01 是非国王红色,10 是黑色王,11 是王红色。这给了我们 64,这是一个很好的数字(长整数的精确大小)。
但是,每个检查器还需要一个额外的位...isOccupied 位,因为每个检查器的位置可以为空,也可以填充上述四种状态之一。我决定将 64 个状态放入一个 long 64 位 int 中,将 32 个占用状态放入一个 32 位整数中。
现在我们有了一些背景知识,我有以下问题:我想轻松地说“这个棋盘上有多少红色棋子?”好吧,这还不错……我们的 64 位整数包含这样的数据:
king_color_king_color_king_color 所以011001 表示我们有红色,黑色国王,红色。
要获取颜色信息,我们可以使用 01010101...01 的位掩码,即十六进制的 0x5555555555555555。这将国王位清零,只留下颜色位。因此,对于 011001 示例,在与掩码进行 AND 运算后,我们得到 010001。如果我们计算位数(popcount,bitcount),我们会得到红色的数量...
啊,等一下!这些颜色可能未“使用中”。我们必须检查我们的 32 位 int 以查看给定的检查器是否正在使用中!所以说我们有 011 代表我们占用的 32 整数……这意味着第一个检查器,上面的 01(红色非国王)……实际上没有被占用……它只是一个空方格。如果我们要在那里移动另一个检查器,我们可能需要也可能不需要更新这 2 个王色位。所以把它们放在一起
32bit = 011
64bit = 011001
代表 3 个棋子位置...一个空棋子,前面是红色,后面是黑色国王,后面是红色。一旦我们在 64 位上执行 010101 掩码操作,我们就会得到:
64bitWithMask = 010001
32bit=011
天真地我们有 2 个红色......但实际上我们只有 1 个活动......我想做的基本上是取 64 位字符串中的奇数位,并将它们与 32 位字符串中的每个位相加...即
1 AND 0, 0 AND 1, 1 AND 1 给我们 001 代表红色跳棋的数量。
或等效地,将64bitWithMask 转换为64bitWithMaskOddBits = 101
然后简单地用 32 位与得到011 & 101 = 001。
那么正式地,有没有办法取一个长度为 2X 的位串,并通过只取奇数位将其减少到长度 X?我正在努力避免循环、ifs 等,并且只使用逻辑(与、或、异或、否定等)。
当然,如果有另一种策略可以在给定我的 32 位和 64 位字符串的情况下获得正确的红色计数。 谢谢!
编辑:
我提出的问题的解决方案在下面接受的答案中得到了很好的解决,但是对于我的实际应用来说,更好的解决方案是将 64 位表示拆分为两个 32。这为我节省了大量操作来提取我需要的内容.感谢 LukeG 和 Tehtmi 的帮助!我很高兴接触到这种“并行”位操作新技术。
【问题讨论】:
-
奇数位移位运算符,与适当的掩码进行与运算
-
或者您可以首先将这些图层分开存储。为什么不使用一个 32 位整数作为 king-bit,一个作为 color-bit,一个作为occupation-bit?
-
为什么不将数据存储在两个 64 位整数中?好的,剩下 32 位未使用,但您也可以组织如何使用/设置这些位,以便进行简单的按位比较。或者干脆使用 3 个 32 位整数,而不是在 64 位变量中交错。
-
是否允许使用
pdep或pext? -
您是否考虑过使用
std::bitset而不是纯整数?它提供了一些可能有帮助的元素函数,尽管没有收集奇数位或偶数位。此外,如果您出于某种原因确实希望将它们放在一个位置,则可以将所有 96 位放入一个std::bitset。
标签: c++ bit-manipulation bit-shift