代码之家  ›  专栏  ›  技术社区  ›  Dave Mateer

位数组相等

  •  5
  • Dave Mateer  · 技术社区  · 15 年前

    我需要的不仅仅是 System.Collections.BitArray 在我的应用程序中。具体来说,我需要位数组:

    • 不可改变
    • 使用值语义实现相等

    我自己创造了 struct 在很大程度上复制了 BitArray 实施。(谢谢, .Net Reflector !)

    我不每天处理按位运算,所以我对我的相等实现没有最高程度的信心。(它通过了我正在进行的单元测试,但我可能错过了边缘案例。)我提出的解决方案如下所示。我会感谢其他人的反馈和回答,因为这些反馈和回答可能更正确或更有效。

    就像CLR一样 位数组 , the length 字段是指结构中的位数和 array 字段(或) Array 属性)表示位的32位整数数组。

    [澄清] 我在构造函数和其他方法中选择了简单的路径,这样我就不能依赖于不必要的零位。例如,

    • Not() 通过按位求反实现( ~ )在整数数组元素上。
    • 一个构造函数是可用的,它需要一个长度和布尔值来初始化所有位。如果初始化值为真,我将int数组的所有元素设置为-1(以2的补码表示,由所有1的补码表示)
    • 等。

    因此,我需要在比较中处理(或者更确切地说,忽略)它们。一个很好的解决方案是始终将这些位置零,但在我的情况下,这将导致更多的工作(包括对计算机和我而言!)

    4 回复  |  直到 11 年前
        1
  •  2
  •   Michael Burr    15 年前

    更新 :我下面的原始分析不正确…

    不幸的是,我对 << 32 -C强制左移位运算符将移位数限制为右操作数的下5位(涉及64位左操作数的移位为6位)。所以你的原始代码在C语言中都是明确的和正确的(它是C/C++中未定义的行为)。本质上,这个移位表达式:

    (this.Array[i] << shift)
    

    相当于:

    (this.Array[i] << (shift & 0x1f))
    

    我可能仍然会改变这种转变,以明确(如果没有其他原因,当我6个月后查看代码时,我不会在同样的错误分析中绊倒)使用上面的代码,而不是 if (shift == 32) 检查。

    原始分析:


    好的,下面是第二个答案。最重要的是,我认为您最初的解决方案有一个错误,在这种情况下,您的位长度 ImmutableBitArray 是32位的倍数 true 对于最后两个不同的数组 Int32[] 数组元素。

    例如,考虑 不可变位数组 s的位长度为32位,这是不同的。原文 Equals() 方法将只对一个执行移位操作 Int32 在数组中-但它会将值移位32位,因为

    int shift = 0x20 - (this.length % 0x20);
    

    将计算为32。

    这意味着下一个测试:

    if (this.Array[i] << shift != other.Array[i] << shift)
    

    意志测验 (0 != 0) 因此, return false 不会被执行。

    我会改变你的 等式() 方法类似于以下内容,这不是一个主要的更改-我认为它会处理上面提到的bug并更改一些与样式严格相关的其他事情,因此可能对您没有任何兴趣。还要注意,我还没有真正编译和测试 等式() 方法,所以有100%的可能出现错误(或者至少是语法错误):

    public bool Equals(ImmutableBitArray other)
    {
        if (this.length != other.length)
        {
            return false;
        }
    
        int finalIndex = this.Array.Length - 1;
    
        for (int i = 0; i < finalIndex; i++)
        {
            if (this.Array[i] != other.Array[i])
            {
                return false;
            }
        }
    
        // check the last array element, making sure to ignore padding bits
    
        int shift = 32 - (this.length % 32);
        if (shift == 32) {
            // the last array element has no padding bits - don't shift
            shift = 0;
        }
    
        if (this.Array[finalIndex] << shift != other.Array[finalIndex] << shift)
        {
            return false;
        }
    
        return true;
    }
    

    请注意,严格来说, GetHashCode() 方法没有被窃听,即使它有相同的缺陷,因为即使在位长度是32的倍数时没有正确地混合最后一个元素,equal对象仍然会返回相同的哈希代码。但我还是决定用同样的方式来解决这个缺陷 GethAsHoc() .

        2
  •  2
  •   Michael Burr    15 年前

    如果在 ImmutableBitArray 最后一个元素上未使用的“padding bits”被强制为零。您不需要跳过圈来只检查最后一个元素中的有效位,因为在相同的实例中padding是相同的。

    这样可以简化 Equals() GetHashCode() 方法:

    public bool Equals(ImmutableBitArray other)
    {
        if (this.length != other.length)
        {
            return false;
        }
    
        for (int i = 0; i < this.Array.Length; i++)
        {
            if (this.Array[i] != other.Array[i])
            {
                // since padding bits are forced to zero in the constructor,
                //  we can test those for equality just as well and the valid
                //  bits
                return false;
            }
        }
    
        return true;
    }
    
    
    public override int GetHashCode()
    {
        int hc = this.length;
    
        for (int i = 0; i < this.Array.Length; i++)
        {
            // since padding bits are forced to zero in the constructor,
            //  we can mix those into the hashcode no problem
    
            hc ^= this.Array[i];
        }
    
        return hc;
    }
    
        3
  •  1
  •   Dave Mateer    15 年前

    等同性方法:

    public bool Equals(ImmutableBitArray other)
    {
        if (this.length != other.length)
        {
            return false;
        }
    
        for (int i = 0; i < this.Array.Length; i++)
        {
            if (this.Array[i] != other.Array[i])
            {
                // This does not necessarily mean that the relevant bits of the integer arrays are different.
                // Is this before the last element in the integer arrays?
                if (i < this.Array.Length - 1)
                {
                    // If so, then the objects are not equal.
                    return false;
                }
    
                // If this is the last element in the array we only need to be concerned about the bits
                // up to the length of the bit array.
                int shift = 0x20 - (this.length % 0x20);
                if (this.Array[i] << shift != other.Array[i] << shift)
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    

    以及必要的gethashcode重写:

    public override int GetHashCode()
    {
        int hc = this.length;
    
        for (int i = 0; i < this.Array.Length; i++)
        {
            if (i < this.Array.Length - 1)
            {
                hc ^= this.Array[i];
            }
            else
            {
                int shift = 0x20 - (this.length % 0x20);
                hc ^= this.Array[this.Array.Length - 1] << shift;
            }
        }
    
        return hc;
    }
    
        4
  •  1
  •   user2434918    11 年前

    经过几个小时的搜索和学习,我终于得到了我的答案,想和大家分享一下。因为我只关心可读性,所以我没有回顾过性能。

    if (input1.length != input2.length)
    {
        return false;
    }
    
    var result = new BitArray(input1);
    result = result.Xor(input2);
    
    if (result.Cast<bool>().Contains(true))
        return false;
    return true;
    
    推荐文章