位数组相等

发布于 2024-08-22 14:53:48 字数 888 浏览 6 评论 0原文

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

  • 保持不可变
  • 为了使用值语义实现相等性,

我创建了自己的 struct,主要复制了 BitArray 实现的内部结构。 (谢谢,.Net Reflector!)

我并不每天处理按位运算,所以我对我的平等实施没有最大程度的信心。 (它通过了我扔给它的单元测试,但我可能会错过边缘情况。)我提出的解决方案如下。我很感激其他人的反馈和答案,这些反馈和答案可能更正确或更有效。

就像 CLR BitArray 一样,length 字段指的是结构体和 array 字段(或 Array< /code> property) 指的是表示位的 32 位整数数组。

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

  • Not() 是通过对整数数组元素进行按位求反 (~) 来实现的。
  • 可以使用一个构造函数,它采用长度和布尔值来初始化所有位。如果初始化值为true,我将int数组的所有元素设置为-1(以二进制补码表示,用全1表示)
  • 等等。

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

I need something a little more than the System.Collections.BitArray class in my application. Specifically, I need the bit array:

  • To be immutable
  • To implement equality using value semantics

I created my own struct, largely copying the internals of the BitArray implementation. (Thanks, .Net Reflector!)

I don't deal everyday with bitwise operations, so I don't have the highest degree of confidence in my equality implementation. (It's passing the unit tests I am throwing at it, but I may be missing edge cases.) I have my proposed solutions as answers below. I would appreciate others' feedback and answers for something that may be more correct or efficient.

Just like the CLR BitArray, the length field refers to the number of bits in the struct and the array field (or Array property) refers to the 32-bit integer array that represents the bits.

[CLARIFICATION] I have chosen to take the easy route in my constructors and other methods so that I cannot rely on the unnecessary bits being zeros. For example,

  • Not() is implemented by bitwise negation (~) on the integer array elements.
  • A constructor is available that takes a length and boolean to initialize all bits. If the initialization value is true, I set all elements of the int array to -1 (in two's complement, represented by all 1's)
  • Etc.

Thus, I need to handle (or, rather, ignore) them in the comparison. A fine solution would also be to keep those bits zeroed at all times, but in my situation that will result in more work (both for the computer and me!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

毁梦 2024-08-29 14:53:48

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

不幸的是,我对 << 的行为是不正确的。 32 - C# 强制左移运算符将移位次数限制为右操作数的低 5 位(涉及 64 位左操作数的移位为 6 位)。因此,您的原始代码在 C# 中定义良好且正确(在 C/C++ 中是未定义的行为)。本质上,这个移位表达式:

(this.Array[i] << shift)

相当于:

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

我可能仍然会更改移位以使其明确(如果没有其他原因,当我在 6 个月后查看该代码时,我不会偶然发现相同的错误分析)使用上面的内容代替了 if (shift == 32) 检查。

原始分析:


好的,这是第二个答案。最重要的是,我认为你原来的解决方案有一个错误,如果你的 ImmutableBitArray 的位长度是 32 位的倍数,你将返回 true 为 2最后一个 Int32[] 数组元素不同的数组。

例如,考虑位长度为 32 位的不同的 ImmutableBitArray。原始的 Equals() 方法将对数组中唯一的一个 Int32 执行移位操作 - 但它会将值移位 32 位,因为

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

计算结果为 32这

意味着下一个测试:

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

将测试 (0 != 0),因此 return false 将不会被执行。

我会将您的 Equals() 方法更改为如下所示,这不是一个重大更改 - 我认为它解决了上述错误并更改了其他一些严格样式的内容-相关,因此您可能没有任何兴趣。另请注意,我尚未实际编译和测试我的 Equals() 方法,因此几乎 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 的倍数时,即使你没有正确地混合最后一个元素,相等的对象仍然会返回相同的哈希码。但我仍然可能决定在 GetHashCode() 中以相同的方式解决该缺陷。

Update: my original analysis below was incorrect...

Unfortunately, I was incorrect about the behavior of << 32 - C# enforces that the left-shift operator will restrict the number of shift to the lower 5 bits of the right operand (6 bits for a shift involving a 64-bit left operand). So your original code was both well-defined and correct in C# (it is undefined behavior in C/C++). Essentially, this shift expression:

(this.Array[i] << shift)

is equivalent to:

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

I'd probably still change the shift to make that explicit (if for no other reason that when I looked at that code 6 months later I wouldn't stumble through the same mistaken analysis) using the above instead of the if (shift == 32) check.

The original analysis:


OK, so here's a second answer. Most importantly, I think that you original solution has a bug in the case where the bit-length of your ImmutableBitArray is a multiple of 32 bits you'll return true for 2 arrays that differ in the last Int32[] array element.

For example, consider ImmutableBitArrays with a bit length of 32 bits that are different. The original Equals() method will perform the shift operation on the one and only Int32 in the array - but it'll shift the values 32 bits, since

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

will evaluate to 32.

That means the next test:

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

Will test for (0 != 0) and therefore the return false won't be executed.

I'd change your Equals() method to something like the following, which isn't a major change - I think it takes care of the above mentioned bug and changes a couple other things that are strictly style-related so may not have any interest to you. Also note that I haven't actually compiled and test my Equals() method, so there's a nearly 100% chance that there's a bug (or at least a syntax error):

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;
}

Note that strictly speaking, the original GetHashCode() method isn't bugged even though it has the same flaw, because even if you don't properly mix in the last element when the bit-length is a multiple of 32, equal object would still return the same hashcode. But I'd still probably decide to address the flaw in the same way in GetHashCode().

酒绊 2024-08-29 14:53:48

如果在 ImmutableBitArray 的构造函数中,最后一个元素上未使用的“填充位”被强制为零,那么您不需要跳过圈子来仅检查最后一个元素中的有效位,因为在相同的情况下,填充将是相同的。

这将很好地简化 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;
}

If in the constructor of the ImmutableBitArray the unused 'padding bits' on the last element are forced to zero you don't need to jump through hoops to only check the valid bits in the last element, as the padding will be the same in equal instances.

That'll simplify the Equals() and GetHashCode() methods nicely:

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;
}
金兰素衣 2024-08-29 14:53:48

经过几个小时的搜索和研究,我终于得到了答案,并分享给大家。我没有审查性能,因为我只关心可读性。

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;

After several hours of searching and study, I finally got my answer and would like to share. I have not reviewed the performance as I only care readability.

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;
李白 2024-08-29 14:53:48

相等方法:

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;
}

The equality method:

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;
}

And the necessary GetHashCode override:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文