C# 位运算练习

C# 位运算练习,c#,bit-manipulation,bitwise-or,C#,Bit Manipulation,Bitwise Or,我有以下练习:数字n0到n7是用二进制表示的字节。这项任务是每一个位子要么下降到底部,要么如果它遇到另一个位子,它就停留在上面。以下是一个可视示例: 我意识到,如果我对从n0到n7的所有数字应用按位OR,则n7的结果总是正确的: n7 = n0 | n1 | n2 | n3 | n4 | n5 | n6 | n7; Console.WriteLine(n7); // n7 = 236 不幸的是,对于其余的字节n6、n5、n4、n3、n2、n1、n0,我想不出正确的方法。 你有什么想法吗?数一

我有以下练习:数字n0到n7是用二进制表示的字节。这项任务是每一个位子要么下降到底部,要么如果它遇到另一个位子,它就停留在上面。以下是一个可视示例:

我意识到,如果我对从n0到n7的所有数字应用按位OR,则n7的结果总是正确的:

n7 = n0 | n1 | n2 | n3 | n4 | n5 | n6 | n7;
Console.WriteLine(n7); // n7 = 236
不幸的是,对于其余的字节n6、n5、n4、n3、n2、n1、n0,我想不出正确的方法。
你有什么想法吗?

数一数每列的1位数字。接下来,清除列并从底部添加正确数量的“代币”。

根据CodesInChaos的建议:

static class ExtensionMethods {
    public static string AsBits(this int b) {
        return Convert.ToString(b, 2).PadLeft(8, '0');
    }
}

class Program {
    static void Main() {
        var intArray = new[] {0, 64, 8, 8, 0, 12, 224, 0 };
        var intArray2 = (int[])intArray.Clone();
        DropDownBits(intArray2);

        for (var i = 0; i < intArray.Length; i++)
            Console.WriteLine("{0} => {1}", intArray[i].AsBits(),
                intArray2[i].AsBits());
    }

    static void DropDownBits(int[] intArray) {
        var changed = true;

        while (changed) {
            changed = false;
            for (var i = intArray.Length - 1; i > 0; i--) {
                var orgValue = intArray[i];
                intArray[i] = (intArray[i] | intArray[i - 1]);
                intArray[i - 1] = (orgValue & intArray[i - 1]);
                if (intArray[i] != orgValue) changed = true;
            }
        }
    }
}
我们从最下面一行开始(i=2)。通过对上面(i-1)的行应用按位or,我们确保第2行中所有0的位都将变为1,如果第1行中的位为1。所以我们让第1行中的1位降到第2行

1) 0101
2) 0110
第1行的右位可能会下降,因为第2行中有“房间”(a
0
)。因此,第2行变为第2行或第1行:
0110 | 0101
,即
0111

现在我们必须移除从第1行掉落的碎片。因此,我们对第2行和第1行的原始值执行位and运算。所以
0110&0101
变成
0100
。由于第2行的值已更改,
changed
变为
true
。 目前的结果如下

1) 0100
2) 0111
这就结束了
i
=2的内部循环。 然后
i
变为1。现在我们让第0行的位下降到第1行

0) 1010
1) 0100
第1行成为第1行或第0行的结果:
0100 | 1010
,即
1110
。第0行成为这两个值上按位and的结果:
0100和1010
is
0000
。同样,当前行已更改

0) 0000
1) 1110
2) 0111
正如你所看到的,我们还没有完成。这就是while(changed)循环的作用。我们从第二排重新开始

第2行=
0111 | 1110=1111
,第1行=
0111&1110=0110
。行已更改,因此
changed
true

0) 0000
1) 0110
2) 1111
然后
i
变为1。第1行=
0110 | 0000=0110
,第0行=
0110&0000=0000
。第1行没有更改,但
changed
的值已经是
true
并保持不变

这一轮的
while(changed)
循环再次发生了变化,所以我们将再次执行内部循环


这一次,所有行都不会更改,导致
changed
剩余
false
,从而结束
while(changed)
循环。

此解决方案仅使用位运算符:

class Program
{
    static void Main(string[] args)
    {
        int n0, n1, n2, n3, n4, n5, n6, n7;
        int n0_, n1_, n2_, n3_, n4_, n5_, n6_, n7_;

        // Input data
        n0 = 0;
        n1 = 64;
        n2 = 8;
        n3 = 8;
        n4 = 0;
        n5 = 12;
        n6 = 224;
        n7 = 0;

        for (int i = 0; i < 7; i++)
        {
            n0_ = n0 & n1 & n2 & n3 & n4 & n5 & n6 & n7;
            n1_ = (n1 & n2 & n3 & n4 & n5 & n6 & n7) | n0;
            n2_ = (n2 & n3 & n4 & n5 & n6 & n7) | n1;
            n3_ = (n3 & n4 & n5 & n6 & n7) | n2;
            n4_ = (n4 & n5 & n6 & n7) | n3;
            n5_ = (n5 & n6 & n7) | n4;
            n6_ = (n6 & n7) | n5;
            n7_ = n7 | n6;

            n0 = n0_;
            n1 = n1_;
            n2 = n2_;
            n3 = n3_;
            n4 = n4_;
            n5 = n5_;
            n6 = n6_;
            n7 = n7_;
        }

        Console.WriteLine("n0: {0}", n0);
        Console.WriteLine("n1: {0}", n1);
        Console.WriteLine("n2: {0}", n2);
        Console.WriteLine("n3: {0}", n3);
        Console.WriteLine("n4: {0}", n4);
        Console.WriteLine("n5: {0}", n5);
        Console.WriteLine("n6: {0}", n6);
        Console.WriteLine("n7: {0}", n7);
    }
}

我想提出一个解决方案,它不会在集合中循环N次,我相信我已经找到了一种新颖的分而治之的方法:

int n0_, n1_, n2_, n3_, n4_, n5_, n6_, n7_;

// Input data
int n0 = 0;
int n1 = 64;
int n2 = 8;
int n3 = 8;
int n4 = 0;
int n5 = 12;
int n6 = 224;
int n7 = 0;

//Subdivide into four groups of 2 (trivial to solve each pair)
n0_ = n0 & n1;
n1_ = n0 | n1;

n2_ = n2 & n3;
n3_ = n2 | n3;

n4_ = n4 & n5;
n5_ = n4 | n5;

n6_ = n6 & n7;
n7_ = n6 | n7;

//Merge into two groups of 4
n0 = (n0_ & n2_);
n1 = (n0_ & n3_) | (n1_ & n2_);
n2 = (n0_ | n2_) | (n1_ & n3_);
n3 = (n1_ | n3_);

n4 = (n4_ & n6_);
n5 = (n4_ & n7_) | (n5_ & n6_);
n6 = (n4_ | n6_) | (n5_ & n7_);
n7 = (n5_ | n7_);

//Merge into final answer
n0_ = (n0 & n4);
n1_ = (n0 & n5) | (n1 & n4); 
n2_ = (n0 & n6) | (n1 & n5) | (n2 & n4);
n3_ = (n0 & n7) | (n1 & n6) | (n2 & n5) | (n3 & n4);
n4_ = (n0) | (n1 & n7) | (n2 & n6) | (n3 & n5) | (n4);
n5_ = (n1) | (n2 & n7) | (n3 & n6) | (n5);
n6_ = (n2) | (n3 & n7) | (n6);
n7_ = (n3 | n7);
这种方法只需要56位操作,这比提供的其他解决方案要少得多

了解最终答案中设置位的情况很重要。例如,如果n5中的列中设置了三个或更多位,则该列为1。这些位可以按任何顺序排列,这使得有效地计算它们相当困难

其思想是将问题分解为子问题,解决子问题,然后将解决方案合并在一起。每次我们合并两个块时,我们知道每个块中的位都会被正确地“删除”。这意味着我们不必在每个阶段检查所有可能的位排列


虽然我直到现在才意识到这一点,但这与合并排序非常相似,合并时它利用已排序的子数组

从下到上迭代,查看连续的行对。将上面的一行替换为二进制和,将下面的一行替换为这两行中的二进制或。重复,直到不再移动。或者在列上迭代,使用
(n[i]>>列)&1
提取并计算1位,然后从底部将该数字的1位填充到列中。我正在考虑这个选项,但我的知识不足以实现它。我的意思是,假设从n0到n7的字节是一个数组的元素。在我们将数组的每个元素转换成二进制后,我们得到:00000000 01000000000011000 000011000 00000000 000011100 1110000000000000我们如何按列处理位?我想你必须在行上循环并提取每个列。备选方案:一个非常简单的解决方案是首先将值转换为二维布尔数组,对其进行操作,然后再转换回来。效率不高,但可能还是更好的解决方案。谢谢,comecme!它工作得很好,但对我来说太复杂了:)我试着解释它是如何工作的。然而,正如人们常说的,如果你的代码需要那么多解释,那就太复杂了。我已经不再需要将结果转换为字节。首先,我使用的是
byte
。但对两个字节进行逐位运算的结果是
int
。这就是为什么我必须使用
(byte)(byteArray[I]| byteArray[I-1])
。感谢您的详细解释。现在清楚了。我以前缺少的是一个基本概念:第N行=第N行|第N-1行;第N-1行=第N行和第N-1行;我知道会有简单的解决办法。。。谢谢@hoang!我喜欢你的方法。
class Program
{

    static void Main(string[] args)
    {
        int n0, n1, n2, n3, n4, n5, n6, n7;
        int n0_, n1_, n2_, n3_, n4_, n5_, n6_, n7_;

        n0 = 0;
        n1 = 64;
        n2 = 8;
        n3 = 8;
        n4 = 0;
        n5 = 12;
        n6 = 224;
        n7 = 0;

        n0_ = n0 & n1 & n2 & n3 & n4 & n5 & n6 & n7;
        n1_ = (n1 & n2 & n3 & n4 & n5 & n6 & n7) | n0;
        n2_ = (n2 & n3 & n4 & n5 & n6 & n7) | n1;
        n3_ = (n3 & n4 & n5 & n6 & n7) | n2;
        n4_ = (n4 & n5 & n6 & n7) | n3;
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n0 = n0_;
        n1 = n1_;
        n2 = n2_;
        n3 = n3_;
        n4 = n4_;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n0: {0}", n0);
        n1_ = (n1 & n2 & n3 & n4 & n5 & n6 & n7) | n0;
        n2_ = (n2 & n3 & n4 & n5 & n6 & n7) | n1;
        n3_ = (n3 & n4 & n5 & n6 & n7) | n2;
        n4_ = (n4 & n5 & n6 & n7) | n3;
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n1 = n1_;
        n2 = n2_;
        n3 = n3_;
        n4 = n4_;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n1: {0}", n1);
        n2_ = (n2 & n3 & n4 & n5 & n6 & n7) | n1;
        n3_ = (n3 & n4 & n5 & n6 & n7) | n2;
        n4_ = (n4 & n5 & n6 & n7) | n3;
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n2 = n2_;
        n3 = n3_;
        n4 = n4_;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n2: {0}", n2);
        n3_ = (n3 & n4 & n5 & n6 & n7) | n2;
        n4_ = (n4 & n5 & n6 & n7) | n3;
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n3 = n3_;
        n4 = n4_;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n3: {0}", n3);
        n4_ = (n4 & n5 & n6 & n7) | n3;
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n4 = n4_;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n4: {0}", n4);
        n5_ = (n5 & n6 & n7) | n4;
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n5 = n5_;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n5: {0}", n5);
        n6_ = (n6 & n7) | n5;
        n7_ = n7 | n6;
        n6 = n6_;
        n7 = n7_;
        Console.WriteLine("n6: {0}", n6);
        n7_ = n7 | n6;
        n7 = n7_;
        Console.WriteLine("n7: {0}", n7);
    }
}
int n0_, n1_, n2_, n3_, n4_, n5_, n6_, n7_;

// Input data
int n0 = 0;
int n1 = 64;
int n2 = 8;
int n3 = 8;
int n4 = 0;
int n5 = 12;
int n6 = 224;
int n7 = 0;

//Subdivide into four groups of 2 (trivial to solve each pair)
n0_ = n0 & n1;
n1_ = n0 | n1;

n2_ = n2 & n3;
n3_ = n2 | n3;

n4_ = n4 & n5;
n5_ = n4 | n5;

n6_ = n6 & n7;
n7_ = n6 | n7;

//Merge into two groups of 4
n0 = (n0_ & n2_);
n1 = (n0_ & n3_) | (n1_ & n2_);
n2 = (n0_ | n2_) | (n1_ & n3_);
n3 = (n1_ | n3_);

n4 = (n4_ & n6_);
n5 = (n4_ & n7_) | (n5_ & n6_);
n6 = (n4_ | n6_) | (n5_ & n7_);
n7 = (n5_ | n7_);

//Merge into final answer
n0_ = (n0 & n4);
n1_ = (n0 & n5) | (n1 & n4); 
n2_ = (n0 & n6) | (n1 & n5) | (n2 & n4);
n3_ = (n0 & n7) | (n1 & n6) | (n2 & n5) | (n3 & n4);
n4_ = (n0) | (n1 & n7) | (n2 & n6) | (n3 & n5) | (n4);
n5_ = (n1) | (n2 & n7) | (n3 & n6) | (n5);
n6_ = (n2) | (n3 & n7) | (n6);
n7_ = (n3 | n7);