我试图传达两个字节流之间的差异。我想最小化补丁中的字节数。
(我不一定希望最小化差异中“变化”的数量,这是levenshtein距离计算中的最佳补丁会给我的。)
理想情况下,补丁的格式应该是,在给定源字节流和差异的情况下,重建目标字节流很容易。
这样做有很好的算法吗?
编辑:为了记录,我尝试发送“在506处的表单的更改,插入以下字节......”,在那里我从levenshtein距离算法创建一个更改列表。
我遇到的问题是levenshtein距离算法给了我很多改变,如:
at spot 506 substitute [some bytes1]
at spot 507 do nothing
at spot 508 substitute [some bytes2]
at spot 509 do nothing
at spot 510 substitute [some bytes3]
...
这是因为lev距离算法试图最小化变化的数量。但是,就我的目的而言,这个指令集是浪费的。如果算法只是说,那可能会更好,
At spot 506 substitute [some bytes1, [byte at spot 507], some bytes2, [byte at spot 509], some bytes3, ...]
可能有一些方法来修改lev距离以支持这些类型的更改,但这似乎有点棘手。我可以在获得更改列表之后合并替换(我将尝试这样做)但是也可能有机会合并删除/插入,并且不太明显如何正确地执行此操作。
只是想知道是否有一个特殊目的算法(或者如果某人已经修改了lev距离以支持这些类型的更改)。
答案 0 :(得分:2)
你可以使用成对对齐与仿射间隙成本来做到这一点,分别为两个长度为n和m的字符串花费O(nm)时间。
首先要做的事情是:根据使用的位或字节,无法找到可证明最小的补丁。这是因为如果有这样的方法,那么计算它的函数shortest_patch(x, y)
可以用于通过用s
和Kolmogorov调用它来找到任何给定字符串shortest_patch('', s)
的可证明的最小压缩。复杂性告诉我们,给定字符串的最短可能压缩是正式无法计算的。但是如果编辑倾向于聚集在空间中,就像它们似乎在这里一样,那么找到比使用通常的Levenshtein距离算法生成的补丁更小的补丁肯定是可能的。
补丁通常在CS中称为“编辑脚本”。查找最小值(根据插入次数加上删除次数)编辑脚本以将一个字符串x
转换为另一个字符串y
等同于找到最佳的成对对齐每对相等字符的值为0,每对不相等的字符的值为-inf,而一个字符串中的字符与-
间隙字符对齐的每个位置的值为-1。对齐很容易想象:
st--ing st-i-ng
stro-ng str-ong
这些是字符串sting
和strong
的2个最佳对齐方式,每个字符串在模型下的成本为-3。如果给出不等字符对的值为-1而不是-inf,那么我们得到一个成本等于Levenshtein距离的对齐(插入的数量,加上删除的数量,加上替换的数量):
st-ing sti-ng
strong strong
这些是新模型下的2个最佳对齐方式,每个方案的成本为-2。
要了解这些与编辑脚本的对应关系,我们可以将顶部字符串视为“原始”字符串,将底部字符串视为“目标”字符串。包含不相等字符对的列对应于替换,顶行中包含-
的列对应于字符的插入,而底行中包含-
的列对应于字符的删除。您可以使用“指令”(C)opy,(D)elete,(I)nsert和(S)ubstitute从对齐创建编辑脚本。每条指令后面都有一个数字,表示从对齐中消耗的列数,在I和S的情况下,要插入或替换相应数量的字符。例如,前两个路线的编辑脚本是
C2, I1"r", S1"o", C2 and C2, S1"r", I1"o", C2
现在,如果我们有mississippi
和tip
等字符串,我们会发现两个路线
mississippi
------tip--
mississippi
t---i----p-
两者具有相同的-9分:它们都需要相同的插入,删除和替换总数。但我们更喜欢顶级的,因为它的编辑脚本可以更加简洁地描述:D6, S1"t", C2, D2
。第二个编辑脚本为S1"t", D3, C1, D4, C1, D1
。
为了使对齐算法也“偏好”第一个对齐,我们可以调整间隙成本,以便开始一个间隙块比继续现有的间隙块花费更多。如果我们这样做,当前一列没有间隙时,包含间隙的列成本为-2而不是-1,那么我们实际做的是惩罚连续的间隙块的数量(因为每个连续的间隙块必须明显有第一个位置)。在这个模型下,上面的第一个对齐成本为-11,因为它包含两个连续的间隙块。第二个对齐现在成本为-12,因为它包含三个连续的间隙块。 IOW,算法现在更喜欢第一次对齐。
这个模型,其中每个包含间隙成本g的对齐位置和任何连续的间隙列块中的第一个位置花费g + s,称为仿射间隙成本模型,并给出了O(nm)算法这是由Gotoh在1982年:http://www.genome.ist.i.kyoto-u.ac.jp/~aln_user/archive/JMB82.pdf。增加缺口开放成本将导致对齐的细分市场聚集在一起。您可以使用各种成本参数,直到获得经验上看起来正确并且足够小的对齐(对应于补丁)。
答案 1 :(得分:1)
解决此类问题有两种方法:
1)为X
建立一种语言(在这种情况下编辑脚本),并弄清楚如何最小化适用句子的长度;或者,
2)计算Y
(字符串差异)的某种最小表示形式,然后想出一种以最短形式表示的方法。
迈尔斯论文表明,对于特定语言,找到最小变化集并找到变化表示的最小长度是同样的问题。
显然,更改语言可能会使该假设无效,并且某些更改可能会非常复杂以便正确应用(例如,假设语言包含原语kP
,这意味着删除下一个k
个字符对于某些差异,使用该原语可能会成为一个巨大的胜利,但应用程序可能非常罕见。这是一个荒谬的例子,我知道,但它证明了从一种语言开始的难度。
所以我建议从最小更改列表开始,该列表标识插入和删除。我们以简单的方式将其转换为一串命令,其中恰好有三个。这里没有指数。我们的想法是,我们从原始字符串开头的光标开始,然后按顺序执行命令。命令是:
= Advance the cursor without altering the character it points to
Ic Insert the character `c` before the cursor.
D Delete the character at the cursor.
虽然我说过有三个命令,但这并不完全正确;实际上A+2
其中A
是字母表的大小。
这可能会产生如下字符串:
=========================IbIaInIaInIaDD=D=D============================
现在,让我们尝试压缩它。首先,我们运行长度编码(RLE),以便每个命令前面都有一个重复计数,然后我们删除尾随=
s
27=1Ib1Ia1In1Ia1In1Ia2D1=1D1=1D
(实际上,RLE会重新创建索引,尽管它们是相对的而不是绝对的。)
最后,我们使用zlib来压缩生成的字符串。我不打算在这里做,只是想知道它可能出现的那种压缩:
27=1Ib1Ia1In||2D1=1D|
______+| ____+
___<---+
(试图展示背面参考。这不是很好的ascii艺术,对不起。)
Liv-Zempell非常善于发现和优化意外重复。实际上,我们可以使用它而不是进行中间RLE步骤,但经验表明,在RLE非常有效的情况下,LZ RLE比源更好。但是值得尝试两种方式来看看哪种方式更适合您的应用。
答案 2 :(得分:0)
使用极少数字节(但不一定是理论上的最佳字节数)的常用方法如下:
假设您拥有的补丁是对文件的一小部分的本地化更改集,这将导致非常短的补丁,因为该文件的大部分将为零,这可以被有效压缩。
要应用补丁,只需解压缩XORed字符串,然后将其与字节流进行异或补丁即可。这计算
原始异或(原始XOR新)=(原始XOR原稿)XOR新=新
由于XOR是关联和自反转的。
希望这有帮助!
答案 3 :(得分:0)
有一种新的改变检测方法。 序列比对问题被认为是协作文本编辑中的变化检测的抽象模型,旨在最小化合并冲突的可能性。新的成本函数定义为检测到的变化与随机字符串之间的交叉概率。 结果应该与其他已知方法的贴片长度最小化更相似。 它避免了LCS和其他方法的已知缺点。 已经提出了立方算法。 http://psta.psiras.ru/read/psta2015_1_3-10.pdf