web-dev-qa-db-ja.com

差分アルゴリズム?

動作し、効率的なdiffアルゴリズムの説明に夢中になっています。

最も近いのは RFC 3284へのこのリンク (Eric Sinkのいくつかのブログ投稿から)です。これは、完全に理解できる用語でdata formatdiffの結果が保存されます。ただし、diffを実行中にプログラムがこれらの結果に到達する方法については一切言及していません。

私は個人的な好奇心からこれを研究しようとしています。なぜなら、diffアルゴリズムを実装するときにはトレードオフが必要だと確信しているからです。その代わりに?」...

VCDIFFを出力する効率的なアルゴリズムの説明はどこにありますか?
ところで、たまたまSourceGearのDiffMergeで使用されている実際のアルゴリズムの説明を見つけたら、それはさらに良いことです。

注:最長共通サブシーケンスは、VCDIFFで使用されるアルゴリズムではないようです。使用するデータ形式を考えると、よりスマートな処理を行っているように見えます。

160
Daniel Magliola

O(ND)差分アルゴリズムとそのバリエーション は素晴らしい論文であり、そこから始めてください。これには、擬似コードと、差分の実行に関係するグラフ走査の素晴らしい視覚化が含まれます。

論文のセクション4は、アルゴリズムを非常に効果的にするためのいくつかの改良点を紹介しています。

これを成功裏に実装すると、ツールボックスに非常に便利なツールが残ります(そして、おそらくいくつかの素晴らしい経験もあります)。

必要な出力形式を生成するのは難しい場合がありますが、アルゴリズムの内部を理解していれば、必要なものをすべて出力できるはずです。ヒューリスティックを導入して、出力に影響を与え、特定のトレードオフを行うこともできます。

ここにページがあります 少しのドキュメント、 完全なソースコード 、および前述のアルゴリズムのテクニックを使用したdiffアルゴリズムの例が含まれています。

ソースコード は、基本的なアルゴリズムに密接に従っているようで、読みやすいです。

入力の準備についても少し説明がありますが、これは役に立つかもしれません。文字またはトークン(Word)によって異なる場合、出力には大きな違いがあります。

幸運を!

168
jscharf

まず、diffの実際のソースコードを見て、GNUで available を作成します。

そのソースコードが実際にどのように機能するかを理解するために、そのパッケージのドキュメントは、それをインスパイアした論文を参照しています。

基本的なアルゴリズムは、「An O(ND) Difference Algorithm and its Variations」、Eugene W. Myers、「Algorithmica」Vol。 1 No. 2、1986、pp。251-266; 「ファイル比較プログラム」、Webb MillerおよびEugene W. Myers、「Software--Practice and Experience」Vol。 15 No. 11、1985、pp。1025-1040。このアルゴリズムは、「近似文字列マッチングのアルゴリズム」、E。Ukkonen、「Information and Control」Vol。 64、1985、pp。100-118。

論文を読んでから実装のソースコードを見るだけで、その仕組みを理解するのに十分なはずです。

33
paxdiablo

https://github.com/google/diff-match-patch を参照してください

「Diff MatchおよびPatchライブラリは、プレーンテキストの同期に必要な操作を実行する堅牢なアルゴリズムを提供します。..現在、Java、JavaScript、C++、C#、Pythonで利用可能です」

wikipedia.org Diffページ および-" Bram Cohen:diffの問題は解決されました "も参照してください。

30

Diffアルゴリズムを探してここに来て、その後自分で実装しました。申し訳ありませんが、vcdiffについては知りません。

Wikipedia :最長共通サブシーケンスから、diffのような出力を得るための小さなステップです:アイテムがサブシーケンスに存在しないが、元に存在する場合、それは削除されている必要があります。 (以下の「–」マーク。)サブシーケンスに存在しないが、2番目のシーケンスに存在する場合は、追加されている必要があります(「+」マーク)。

LCSアルゴリズムはこちら の素敵なアニメーション。

高速 LCS Ruby実装はこちら へのリンク。

ゆっくりとシンプルなRubyの適応は以下のとおりです。

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]
11
neoneye

Emmelaichが提供したリンクに基づいて、 Neil FraserのWebサイト(ライブラリの作成者の1人) でDiff Strategiesの大まかな要約もあります。

彼は基本的な戦略をカバーし、記事の終わりに向かって、マイヤーのアルゴリズムといくつかのグラフ理論に進みます。

9
Chris S