差异算法?[关闭]


164

我一直在疯狂地解释有效且高效的diff算法。

我得到的最接近的链接是RFC 3284(来自Eric Sink的几篇博客文章),该链接以完全易于理解的术语描述了差异结果存储的数据格式。但是,它没有提及程序在进行比较时如何达到这些结果。

我试图出于个人好奇心进行研究,因为我敢肯定在实施diff算法时必须权衡取舍,当您查看diff并想知道“为什么diff程序为什么选择此作为更改时,这很清楚而不是那个?” ...

在哪里可以找到最终输出VCDIFF的有效算法的描述?
顺便说一句,如果您偶然发现SourceGear的DiffMerge使用的实际算法的描述,那就更好了。

注意:最长的公共子序列似乎不是VCDIFF所使用的算法,在给定使用的数据格式的情况下,它们似乎在做些更聪明的事情。


维基百科上什么都没有?您也许可以尝试以更高级的语言找到另一个实现,例如python,它可能比C实现更容易理解。Python以易于阅读而闻名?python中有一个difflib。这是来源的网址。消息来源对diff算法发表了大量评论。svn.python.org/view/python/trunk/Lib/...
bsergean

4
RFC并非旨在描述算法。它们旨在描述接口(/协议)。

2
实际上,diff算法的核心是最长的常见子序列问题,可以在Wikipedia上找到。本页概述了算法和示例代码,这些代码和示例代码在我需要编写自定义差异时很有用
Corwin Joy

3
也许这会有所帮助:paulbutler.org/archives/a-simple-diff-algorithm-in-php确实很棒,而且它很小(总共只有29行;它有2个功能)。它类似于Stack Overflow的编辑修订比较内容。
内森

VCDIFF不适用于人类可读的差异。它采用添加,复制和运行指令,而不是大多数纯文本diff算法发出的更易读的删除和插入指令。对于VCDIFF你所需要的描述类似的xdelta algortihm xmailserver.org/xdfs.pdf
asgerhallas

Answers:


175

O(ND)差异算法及其变体是一篇很棒的论文,您可能要从这里开始。它包括伪代码和进行差异所涉及的图形遍历的漂亮可视化。

本文的第4节介绍了对该算法的一些改进,使其非常有效。

成功实现这一点将为您提供一个非常有用的工具,并且可能会提供一些出色的经验。

生成所需的输出格式有时可能会有些棘手,但是如果您了解算法的内部原理,那么您应该可以输出所需的任何内容。您还可以引入启发式方法来影响输出并进行一定的权衡。

这是一个页面,其中包含一些文档,完整的源代码以及使用上述算法中的技术的diff算法示例。

源代码似乎紧跟基本算法和易于阅读。

还有一些准备输入的内容,您可能会觉得有用。当您按字符或标记(单词)进行区分时,输出会有巨大差异。

祝好运!


1
万一链接变坏,那就是迈尔斯(Myers)1986;参见例如citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927-它进一步包含了diffHunt和McIlroy 到Unix 论文的链接。
三胞胎

34

我将从查看diff 的实际源代码开始 ,这是GNU 提供的

为了了解源代码的实际工作原理,该软件包中的文档参考了激发源代码的论文:

基本算法在Eugene W. Myers的“ Algorithmica”卷中的“ O(ND)差分算法及其变体”中进行了描述。1 1986年第2号,第251-266页;在“文件比较程序”中,韦伯·米勒(Webb Miller)和尤金·迈尔斯(Eugene W. Myers),“软件-实践与经验”卷。1985年11月15日,第1025-1040页。该算法是独立发现的,如E.Ukkonen的“ Information and Control”卷,“近似字符串匹配算法”中所述。64,1985,第100-118页。

阅读论文,然后查看实现的源代码应该足以理解其工作原理。


80
简而言之,有时可能会从实际的源代码中找出底层算法(尤其是经过优化以提高效率)。我将能够逐步了解程序在做什么,但不完全是“为什么”,或者对此有一个高级概述...示例:您永远不会了解正则表达式是如何工作的(或它们是什么)查看Perl的Regexes的实现。或者,如果您可以这样做,那么请给我戴上帽子,我当然需要一个更解释,更高级的概述来了解发生了什么。
Daniel Magliola 09年

3
我从来不了解绝大多数Perl的工作原理:-),但是包docs(请参阅更新)中有一个链接,可以指向描述它的文献(它是diff算法,而不是Perl)。
paxdiablo

32
不要阅读代码。阅读论文。
Ira Baxter,2009年

31

参见https://github.com/google/diff-match-patch

“ Diff Match和Patch库提供了健壮的算法来执行同步纯文本所需的操作。...目前可用于Java,JavaScript,C ++,C#和Python”

另请参见wikipedia.org Diff页面和-“ Bram Cohen:diff问题已解决


2
只是想提一下,Cohen的算法似乎也被称为Patience Diff。它是Bazaar中的(默认?)diff算法,而git中是可选算法。
Dale Hagglund 2010年

13

我来到这里寻找diff算法,然后实现了自己的实现。抱歉,我不了解vcdiff。

维基百科:从最长的公共子序列中,获得类似diff的输出只是一小步:如果子序列中不存在某项,但该项出现在原始序列中,则必须将其删除。(下面的'–'标记。)如果子序列中不存在,但是在第二个序列中存在,则必须添加它。('+'标记。)

LCS算法的动画效果很好。

在此处链接到快速的LCS红宝石实现

我的缓慢而简单的红宝石改编如下。

def lcs(xs, ys)
  if xs.count > 0 and ys.count > 0
    xe, *xb = xs
    ye, *yb = ys
    if xe == ye
      return [xe] + lcs(xb, yb)
    end
    a = lcs(xs, yb)
    b = lcs(xb, ys)
    return (a.length > b.length) ? a : b
  end
  return []
end

def find_diffs(original, modified, subsequence)
  result = []
  while subsequence.length > 0
    sfirst, *subsequence = subsequence
    while modified.length > 0
      mfirst, *modified = modified
      break if mfirst == sfirst
      result << "+#{mfirst}"
    end
    while original.length > 0
      ofirst, *original = original
      break if ofirst == sfirst
      result << "-#{ofirst}"
    end
    result << "#{sfirst}"
  end
  while modified.length > 0
    mfirst, *modified = modified
    result << "+#{mfirst}"
  end
  while original.length > 0
    ofirst, *original = original
    result << "-#{ofirst}"
  end
  return result
end

def pretty_diff(original, modified)
  subsequence = lcs(modified, original)
  diffs = find_diffs(original, modified, subsequence)

  puts 'ORIG      [' + original.join(', ') + ']'
  puts 'MODIFIED  [' + modified.join(', ') + ']'
  puts 'LCS       [' + subsequence.join(', ') + ']'
  puts 'DIFFS     [' + diffs.join(', ') + ']'
end

pretty_diff("human".scan(/./), "chimpanzee".scan(/./))
# ORIG      [h, u, m, a, n]
# MODIFIED  [c, h, i, m, p, a, n, z, e, e]
# LCS       [h, m, a, n]
# DIFFS     [+c, h, +i, -u, m, +p, a, n, +z, +e, +e]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.