有没有办法在c#.net中的两个字节数组上生成二进制差异?

时间:2009-12-12 16:21:41

标签: c# .net data-structures algorithm

我试图比较内存中的两个字节数组并生成一个数据结构来保存差异,这样对于字节数组B和保持差异的数据结构,就可以重新创建字节数组A.

字节数组的大小始终相同且相对较小。字节数组表示位图,其大小通常在1000x1000x32到128x128x32之间。

速度和效率(来自cpu时间pov),其中可以组合/用于重构字节数组A的数据结构保持差异和第二字节数组是最重要的。差异对象的生成效率并不重要。

现在我的解决方案是使用二进制搜索+ md5散列方法来构建图像内部最小差异单位的列表,并将原始字节数据与其在字节数组A内的偏移量的引用打包。

即。我为Image A的字节数组生成哈希并将其与Image B的字节数组的哈希值进行比较,如果它不匹配我将图像水平分割成两半并散列每个块然后比较图像之间的那些哈希值,这个过程就是以递归方式重复,直到所有不匹配的块达到最小大小,如32x32x32和/或最大分割数。一旦块被标记为匹配,它就不再是递归搜索的一部分。一旦识别出所有差异,它们将被添加到变更列表中,然后该列表就是差异对象。

是否有任何内置方法可以有效地生成我正在寻找的结果?或者是否有任何图书馆可以完成这项工作?


备注:

  • 这是一个学习项目(适用于WCF,WPF和许多其他技术),
  • 这是一个VNC风格的系统 - 因此图像是窗口的快照,将通过LAN连接发送。
  • 重构字节数组A必须高效的原因是客户端可以连接到多个服务器,每个服务器可以服务多个窗口,因此客户端可以监视/与30多个窗口进行交互。
  • 我想在每个窗口中为任何更改的内容实现3 fps +。

3 个答案:

答案 0 :(得分:7)

如果保证它们的大小相同 - 实际上是相同的尺寸 - 那么我没有看到所有这些散列和二进制搜索以及其他开销的重要性。你可以简单地比较一个循环中的两个字节,如果它们不匹配,就在你的diff中添加一个“点”,其中包含索引和A中的值。要反转过程,你不要因为你已有索引,所以需要查看每个字节。

如果两个数组相差仅比1个字节,那么你最终会得到一个大小为5个字节的diff结构(假设你使用Int32作为索引),并且只需要1次迭代来改变B返回到A.通常,对于diff,过程为O(n),对于恢复,过程为O( m ),其中 m 是实际更改的总点数。我不是数据结构方面的专家,但我怀疑你能否提出更有效的方法。

所以,像这样:

Diff GetDiff(byte[] a, byte[] b)
{
    Diff diff = new Diff();
    for (int i = 0; i < a.Length; i++)
    {
        if (a[i] != b[i])
        {
            diff.Points.Add(new DiffPoint(i, a[i]));
        }
    }
    return diff;
}

// Mutator method - turns "b" into the original "a"
void ApplyDiff(byte[] b, Diff diff)
{
    foreach (DiffPoint point in diff.Points)
    {
        b[point.Index] = point.Value;
    }
}

// Copy method - recreates "a" leaving "b" intact
byte[] ApplyDiffCopy(byte[] b, Diff diff)
{
    byte[] a = new byte[b.Length];
    int startIndex = 0;
    foreach (DffPoint point in diff.Points)
    {
        for (int i = startIndex; i < point.Index; i++)
        {
            a[i] = b[i];
        }
        a[point.Index] = point.Value;
        startIndex = point.Index + 1;
    }
    for (int j = startIndex; j < b.Length; j++)
    {
        a[j] = b[j];
    }
    return a;
}

struct DiffPoint
{
    public int Index;
    public byte Value;

    public DiffPoint(int index, byte value) : this()
    {
        this.Index = index;
        this.Value = value;
    }
}

class Diff
{
    public Diff()
    {
        Points = new List<DiffPoint>();
    }

    public List<DiffPoint> Points { get; private set; }
}

ApplyDiffCopy中有很多循环但是如果你解决了,你会发现它实际上每个点只执行一次操作。当然,如果你不需要副本而只想改变B,那么如果实际差异不大,那么第一个ApplyDiff方法将非常快。

显然我在这里没有做太多的错误检查。您可能希望在防御性方面编写您的版本(验证数组长度等)

如果我已正确理解此处的假设以及您尝试解决的问题,那么原始ApplyDiff方法将是恢复原始图像的最快方法。

答案 1 :(得分:2)

Crikey! - 这有点复杂,两个数组的XOR运行长度编码出了什么问题 - 在一次通过中进行编码和解码,并且在空间中应该合理有效,因为大多数值都是零,但你可以重新编写 - 如果需要,进一步压缩RLE数据。

答案 2 :(得分:0)

您可以使用BitArray类(或使用Reflector来查看它是如何实现的,这样您就不会复制数组以便加快它的速度)

        byte[] s1 = new byte[] {0,1,2,3,4,5,6};
        byte[] s2 = new byte[] {0,1,2,4,4,6,6};
        var orig1 = new BitArray(s1);
        var orig2 = new BitArray(s2);
        var diff = orig1.Xor(orig2);
        byte[] diffArray = new byte[s1.Length];
        diff.CopyTo(diffArray, 0); // here we have a byte diff array of s1 and s2

        var reconstruct = orig2.Xor(diff);
        reconstruct.CopyTo(diffArray, 0); // diffArray is now the same as s1