首页 > 解决方案 > 将偶数和奇数分组

问题描述

我正在学习 Linq 以及如何对事物进行分组,我正在查看我以前的解决方案,我发现了这一点:

public IList<IGrouping<int, int>> GroupEvenAndOddNumbers(int[] numbers)
{
    return numbers.ToLookup(x => x & 1).ToList();
}

这与我一直在进行分组的方式背道而驰,但我找不到以正常方式进行分组的方法。问题是我不明白为什么会这样,这不应该是个问题,但是知道为什么会这样会很好。我不明白的部分是“x & 1”部分。这里应用了什么逻辑?

标签: c#linq

解决方案


奇偶校验(奇/偶)

奇偶校验和二进制数之间存在有趣的相互作用。所有奇数以 结尾1,所有偶数以 结尾0

想想十进制系统,以10为底。你怎么知道一个东西能被10整除?因为它以0结尾。
这适用于每个基地。在以 8 为底(八进制)中,可被 8 整除的数字以 0 结尾。在以 16 为底(十六进制)中,可被 16 整除的数字以 0 结尾。以 12345 为底,所有可被 12345 整除的数字以 0 结尾。选择任意数字,它总是有效的。

在以 2 为底(二进制)中,可被 2 整除的数字(= 偶数,根据定义)以 0 结尾。

&是按位与运算符。非常简单地说,您将两个二进制值传递给它,并且对于同一位置的每个数字,它都会进行digitA AND digitB评估。结果就是这些AND评估的价值。

首先,AND只有当两个输入都为真时,评估才会返回真。如果至少有一个是假的,那么结果肯定也是假的。

输入 A 输入 B 结果
0 0 0
1 0 0
0 1 0
1 1 1

现在让我们看两个随机二进制数,对于每个数字位置,找出 AND 结果是什么。

         10110100
         10001011
 -[AND]------------
         10000000

请注意,仅在最左边的位置,两个位都是 1(真),因此结果也是 1(真)。在所有其他位置,两个输入数字之一中始终存在 0(假),因此结果为 0(假)

现在让我们看一个特殊情况。我将隐藏第一个数字,只显示第二个数字。试着弄清楚你已经猜到了多少结果。

         ????????
         00000001
 -[AND]------------
         ????????

似乎不可能,对吧?好吧,让我们考虑一下。在任何位置,如果至少有一个0,则结果肯定是0。即使我们不知道第一个数字,第二个数字已经揭示了前7位的位置肯定会导致0,因为第二个number 已经通过在这些位置使用 0 来强制执行此操作。

所以现在我们知道了:

         ????????
         00000001
 -[AND]------------
         0000000?

让我们想想最后一个数字。什么时候是1,什么时候是0?在阅读下面的答案之前考虑一下。

如果第一个数字以 0 结尾,则结果也将以 0 结尾,因为0 AND 1 = 0.
如果第一个数字以 1 结尾,则结果也将以 1 结尾,因为1 AND 1 = 1

还记得我们之前说过的话吗?在二进制中,所有奇数以 结尾1,所有偶数以 结尾0。所以我们可以重写我们之前的结论说如下:

如果第一个数字是偶数,结果也将以 0 结尾,因为0 AND 1 = 0
如果第一个数字是奇数,结果也将以 1 结束,因为1 AND 1 = 1

我们可以颠倒这个逻辑:

如果结果以 0 结尾,则第一个数字是偶数
如果结果以 1 结尾,则第一个数字是奇数

编码

numbers.ToLookup(x => x & 1).ToList()

ToLookup本质上是一个 group by 语句。它将具有相同计算值的所有元素分组在同一组中。这些组是根据计算值生成的x & 1

这里只有两个可能的组。使用我们的演示值,结果是0000000000000001(因为前 7 位数字始终为 0,而最后一位数字可能不同)。

在标记为 的组00000000中,您会找到以 a 结尾的所有数字0,即所有偶数
在标有 的组00000001中,您会找到以 a 结尾的所有数字1,这些数字都是奇数

组的标签并不重要。这里重要的是你现在已经正确地分开了 evens 和 odds


这里还有其他可能的解决方案,但它们都归结为相同的原理。另一个例子是

var evenNumbers = numbers.Where(x => x % 2 == 0).ToList();
var oddNumbers  = numbers.Where(x => x % 2 == 1).ToList();

但是,位逻辑的性能往往略高一些。这是以位逻辑相当困难为代价的,但在寻找数字奇偶校验(偶数/奇数)这样简单的情况下,性能增益值得轻微的复杂性。


推荐阅读