繁体   English   中英

将64位整数中的所有其他位与32位整数进行比较

[英]Comparing every other bit in a 64 bit integer to a 32 bit integer

我当时正在考虑创建一个小跳棋求解器的想法。 首先,我将制作一个非常紧凑的棋盘格表示形式,然后继续构建游戏树等。

一个标准的棋盘具有8行4列功能 (棋盘只能对角移动)。 这给了我们32个职位! 每个位置需要3位信息... king位和color位...因此00 是非王黑色01 是非王红色10王黑色11王红色 这给了我们64个很好的数字(长整数的精确大小)。

但是,每个检查器还需要一个额外的位... isOccupied位,因为每个检查器位置可以为空,也可以填充上述四个状态之一。 我决定采用64个状态并将它们放入一个长的64位int,将32个占用状态放入一个32位整数。

因此,现在有了一些背景知识,我就会遇到以下问题:我想轻松地说“板上有几个红色的跳棋?” 好吧,这还不错...我们的64位整数保存如下数据:

king_color_king_color_king_color所以011001表示我们有红色,黑色国王,红色。

为了只获取颜色信息,我们可以使用01010101 ... 01的位掩码(十六进制为0x5555555555555555)。 这将国王位清零,而只留下颜色位。 因此,在与掩码进行“与”运算之后的011001示例中,我们得到了010001。如果我们对位数( popcountbitcount )进行计数,我们将获得红色的数量...

啊,等一下! 这些颜色可能不是“使用中”。 我们必须检查我们的32位整数,以查看给定的检查器是否正在使用中! 假设我们有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位进行AND 64bitWithMaskOddBits = 101即可得出011 & 101 = 001

因此,从形式上讲,是否有办法采用长度为2X的位串,并仅采用奇数位将其减小为长度X? 我非常努力地避免循环,ifs等,并且仅使用逻辑(与,或,或,异或等)。

或者,当然,如果考虑到我的32位和64位字符串,还有另一种方法来获取正确的红色计数。 谢谢!

编辑:

我提出的问题的解决方案在下面的可接受的答案中得到了很好的解决,但是对于我的实际应用程序来说,更好的解决方案是将64位表示形式分成两个32。这节省了我很多操作来提取所需的内容。 感谢LukeG和Tehtmi的帮助! 我很高兴接触到这种新的位操作新技术“并行”。

将每个其他位从一个数字压缩成一个半长数字有些棘手,因为每个位需要移位不同的量。 但是,有一种聪明的方法可以实现比单独处理每个位所需的操作更少的操作。 对于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步中移动。

这种位算法有时被称为“并行”。 您可能有兴趣查看此著名清单 (它包括与这里发生的情况非常相关的交织。)

这种代码的标准免责声明是通常很难使用,因此除非有实际的性能问题(即使这样,也请确保清楚代码的含义),否则不应在严肃的项目中使用它。做,例如带注释)。 如果没有性能问题,并且您仍想使用位操作,则循环解决方案仍然是首选,因为它更易于理解和使用。

如果不使用循环,我看不到有任何方法可以做到这一点。

编辑: tehtmi证明我做错了。 我仍然认为,在此答案结尾处提出的“替代解决方案”是解决当前问题的更好方法,tehtmi提出了一个非常有趣的解决方案,如果您还没有,则应该向上滚动并投票赞成。

我看到两种解决方法。

第一个接近您要实现的目标是:

uint32_t occupied;
uint64_t data;

uint32_t occupiedWithRed;
for (auto i = 0; i < 32; ++i) {
    occupiedWithRed |= (data >> i) & occupied & (1 << i);
}

红色位置的计数将是占用红色的置位计数。

更简单(可能更快)的方法是:

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位无符号整数,则可以减轻生活负担。 一种用于区分红色和黑色,一种用于区分自由和被占领,以及一种用于区分国王和不国王。 这样,您的任务将变得更加容易。 这将是一个按位运算并计算汉明权重的问题。

与其收集偶数或奇数位以与32个占用位进行比较,不如选择另一种方式将它们分配到64位整数中,以便它们仅出现在奇数位置。 另外,我会将它们移动到另一个64位整数中的偶数位置。

然后,您可以轻松地将奇数位或偶数位占用整数与位置信息整数中的偶数或奇数位进行比较。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM