简体   繁体   English

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

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

I was playing around with the idea of creating a little checkers solver. 我当时正在考虑创建一个小跳棋求解器的想法。 First I'd make a very compact checkers board representation, then go on to build the game tree and such. 首先,我将制作一个非常紧凑的棋盘格表示形式,然后继续构建游戏树等。

A standard checkers board has 8 rows , and 4 functional columns (checkers can only move diagonal). 一个标准的棋盘具有8行4列功能 (棋盘只能对角移动)。 That gives us 32 positions! 这给了我们32个职位! Each position needs 3 bits of information... king bit, and color bit... so 00 is non-king black , 01 is non-king red , 10 is king black , 11 is king red . 每个位置需要3位信息... king位和color位...因此00 是非王黑色01 是非王红色10王黑色11王红色 That gives us 64 which is a nice number to work with (Exact size of a long integer). 这给了我们64个很好的数字(长整数的精确大小)。

However, each checker also needs one additional bit... the isOccupied bit, since each checkers position can be empty, or filled with one of the four states above. 但是,每个检查器还需要一个额外的位... isOccupied位,因为每个检查器位置可以为空,也可以填充上述四个状态之一。 I decided to take the 64 states and put them into a long 64-bit int, and the 32 occupied states and put them into a 32-bit integer. 我决定采用64个状态并将它们放入一个长的64位int,将32个占用状态放入一个32位整数。

So now that we have some background, I have the following problem: I want to easily say "How many red checkers are on this board?" 因此,现在有了一些背景知识,我就会遇到以下问题:我想轻松地说“板上有几个红色的跳棋?” Well that's not so bad... our 64 bit integer holds data like this: 好吧,这还不错...我们的64位整数保存如下数据:

king_color_king_color_king_color so 011001 means we have red, black king, red. king_color_king_color_king_color所以011001表示我们有红色,黑色国王,红色。

TO get just the color information, we can use a bit mask of 01010101...01 which is 0x5555555555555555 in hex. 为了只获取颜色信息,我们可以使用01010101 ... 01的位掩码(十六进制为0x5555555555555555)。 That zeroes out the king bits, and just leaves the color bits. 这将国王位清零,而只留下颜色位。 So with the 011001 example after ANDing with the mask we have 010001. If we count the bits ( popcount , bitcount ) we get the number of reds... 因此,在与掩码进行“与”运算之后的011001示例中,我们得到了010001。如果我们对位数( popcountbitcount )进行计数,我们将获得红色的数量...

Ah, but wait! 啊,等一下! Those colors may not be "in use". 这些颜色可能不是“使用中”。 We have to check our 32 bit int to see if a given checker is in use! 我们必须检查我们的32位整数,以查看给定的检查器是否正在使用中! So say we have 011 for our occupied 32 integer... that means the first checker, 01 above (red non king)... is actually not occupied... its just an empty square. 假设我们有011个占用的32个整数...这意味着上面的第一个检查器01(红色非国王)...实际上并未被占用...它只是一个空的正方形。 If we were to move another checker there, we may or may not need to update those 2 king-color bits. 如果要在此处移动另一个检查器,则可能需要更新这2个国王色位,也可能不需要。 So putting it all together 所以放在一起

32bit = 011
64bit = 011001

Representing 3 checker positions... an empty checker with was a red before, followed by black king, followed by red. 代表3个棋盘格位置...空棋盘格之前是红色,其次是黑王,然后是红色。 Once we do the 010101 mask operation on 64bit we get: 一旦在64位上执行010101掩码操作,我们将得到:

64bitWithMask = 010001
32bit=011

Naively we have 2 reds... but we actually only have 1 active... what I would like to do is essentially take the odd bits in the 64 bit string, and AND them with each bit in the 32 bit string... ie 天真的我们有2个红色...但是实际上我们只有1个活动...我想要做的基本上是取64位字符串中的奇数位,然后将它们与32位字符串中的每一位相加...即

1 AND 0, 0 AND 1, 1 AND 1 gives us 001 which represents the count of red checkers. 1 AND 0, 0 AND 1, 1 AND 1得到001,它表示红色检查器的数量。

Or equivalently, convert 64bitWithMask to 64bitWithMaskOddBits = 101 Then simply AND with the 32 bit to get 011 & 101 = 001 . 或等效地,将64bitWithMask转换为64bitWithMaskOddBits = 101然后简单地与32位进行AND 64bitWithMaskOddBits = 101即可得出011 & 101 = 001

So formally, is there a way to take a bit string of length 2X, and reduce it to length X by taking only the odd bits? 因此,从形式上讲,是否有办法采用长度为2X的位串,并仅采用奇数位将其减小为长度X? I am trying very hard to avoid loops, ifs, etc, and only using logic (and, or, xor, negation, etc). 我非常努力地避免循环,ifs等,并且仅使用逻辑(与,或,或,异或等)。

Or of course, if there is another strategy to get the proper count of reds given my 32-bit and 64-bit strings. 或者,当然,如果考虑到我的32位和64位字符串,还有另一种方法来获取正确的红色计数。 Thanks! 谢谢!

EDIT: 编辑:

The solution to the problem I posed is elegantly solved below in the accepted answer, but the better solution for my actual application was to split the 64 bit representation into two 32. That saves me a bunch of operations to extract what I need. 我提出的问题的解决方案在下面的可接受的答案中得到了很好的解决,但是对于我的实际应用程序来说,更好的解决方案是将64位表示形式分成两个32。这节省了我很多操作来提取所需的内容。 Thanks to both LukeG and Tehtmi for the help! 感谢LukeG和Tehtmi的帮助! I'm happy to be exposed to this new technique of bit manipulation, "parallel". 我很高兴接触到这种新的位操作新技术“并行”。

Compressing every other bit from a number into a half-length number is a bit tricky since each bit needs to get shifted by a different amount. 将每个其他位从一个数字压缩成一个半长数字有些棘手,因为每个位需要移位不同的量。 However, there is a clever way to do it that requires fewer operations than handling each bit individually. 但是,有一种聪明的方法可以实现比单独处理每个位所需的操作更少的操作。 For 64-bits, it looks like this (pseudo-code): 对于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

Here's an illustration of what's happening to the bits at each step for a 32-bit number (after the initial mask): 这是一个32位数字(在初始掩码之后)每个步骤的位所发生的情况的说明:

0a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p
00ab00cd00ef00gh00ij00kl00mn00op
0000abcd0000efgh0000ijkl0000mnop
00000000abcdefgh00000000ijklmnop
0000000000000000abcdefghijklmnop

For example, bit g needs to be shifted 9 to the right, so look at the power-of-two components 9 = 1 + 8 . 例如,位g需要向右移动9 ,因此请看两个二次方分量9 = 1 + 8 Thus, g gets shifted in the >> 1 step and the >> 8 step. 因此, g>> 1步和>> 8步中移动。

Bit algorithms of this sort are sometimes described as "parallel". 这种位算法有时被称为“并行”。 You may be interested in checking out this famous list . 您可能有兴趣查看此著名清单 (It includes interleaving which is very closely related to what's happening here.) (它包括与这里发生的情况非常相关的交织。)

The standard disclaimer for this sort of code is that it is generally difficult to work with, so it should probably not be used in serious projects unless there is actually a performance issue (and even then, make sure it is clear what the code is supposed to do, eg with comments). 这种代码的标准免责声明是通常很难使用,因此除非有实际的性能问题(即使这样,也请确保清楚代码的含义),否则不应在严肃的项目中使用它。做,例如带注释)。 If there is no performance issue and you still want to use bit operations, then the loop solution may still be preferred as it is easier to understand and work with. 如果没有性能问题,并且您仍想使用位操作,则循环解决方案仍然是首选,因为它更易于理解和使用。

I don't see any way to do this without using a loop. 如果不使用循环,我看不到有任何方法可以做到这一点。

Edit: And I was proven wrong by tehtmi. 编辑: tehtmi证明我做错了。 Wile I still think the "alternative solution" proposed at the end of this answer is the better way to solve the problem at hand, tehtmi presented a very interesting solution and if you haven't yet, you should scroll up and upvote it. 我仍然认为,在此答案结尾处提出的“替代解决方案”是解决当前问题的更好方法,tehtmi提出了一个非常有趣的解决方案,如果您还没有,则应该向上滚动并投票赞成。

I see two ways to approach this. 我看到两种解决方法。

The first one, which comes close to what you want to achieve, is: 第一个接近您要实现的目标是:

uint32_t occupied;
uint64_t data;

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

The count of red positions would be the count of set bits in occupiedWithRed. 红色位置的计数将是占用红色的置位计数。

The easier (and probably faster) way is: 更简单(可能更快)的方法是:

uint32_t occupied;
uint64_t data;

auto count = 0;
for (auto i = 0; i < 32; ++i) {
    if ((data >> (2 * i)) & (occupied > i)) ++count;
}

Or, do something completely different: As was noted in the comments you could ease your life if your separated your data in 3 different 32 bit unsigned integers. 或者,执行完全不同的操作:如注释中所述,如果将数据分成3个不同的32位无符号整数,则可以减轻生活负担。 One for distinguishing red and black, one for distinguishing free and occupied and one for distinguishing between king and no king. 一种用于区分红色和黑色,一种用于区分自由和被占领,以及一种用于区分国王和不国王。 Your task would become significantly easier this way. 这样,您的任务将变得更加容易。 It would be a matter of one bitwise and and a calculating a hamming weight. 这将是一个按位运算并计算汉明权重的问题。

Instead of collecting the even or odd bits to compare with the 32 occupancy bits I'd rather go the other way and distribute them into a 64 bit integer such that they go only on the odd positions. 与其收集偶数或奇数位以与32个占用位进行比较,不如选择另一种方式将它们分配到64位整数中,以便它们仅出现在奇数位置。 Additionally, I'd shift them to the even positions in another 64 bit integer. 另外,我会将它们移动到另一个64位整数中的偶数位置。

Then you can easily either compare the odd-positioned or even-positioned occupancy integer to the even or odd bits in the position information integer. 然后,您可以轻松地将奇数位或偶数位占用整数与位置信息整数中的偶数或奇数位进行比较。

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

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