差分アルゴリズム?[閉まっている]


164

私は、効果的で効率的なdiffアルゴリズムの説明に夢中になっています。

私が得た最も近いのは、RFC 3284へのリンク(Eric Sinkのいくつかのブログ投稿から)です。これは、diffの結果が格納されるデータ形式を完全に理解できる用語で説明しています。ただし、diffを実行しているときにプログラムがこれらの結果に到達する方法については、まったく触れられていません。

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

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

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


ウィキペディアには何もありませんか?Cの実装よりも理解しやすいかもしれない、Pythonのような高レベルの言語で別の実装を探すこともできます。Pythonは読みやすいことで有名ですか?Pythonにはdifflibがあります。これがソースのURLです。ソースには、diffアルゴリズムに関する大量のコメントがあります。svn.python.org/view/python/trunk/Lib/...
bsergean

4
RFCはアルゴリズムを説明するためのものではありません。それらはインターフェース(/プロトコル)を記述することを意図しています。

2
実際、最も長い共通のサブシーケンス問題であるdiffアルゴリズムの中核は、Wikipediaにあります。このページは、アルゴリズムの概要と、カスタムdiffを記述する必要があるときに役立つサンプルコードを示しています。en.wikipedia.org
wiki

3
おそらくこれが役立つでしょう:paulbutler.org/archives/a-simple-diff-algorithm-in-php確かに素晴らしいです、そしてそれはとても小さいです(全部29行だけです; 2つの機能があります)。Stack Overflowの編集リビジョンの比較と似ています。
ネイサン

VCDIFFは人間が読める形式の差分ではありません。ほとんどのプレーンテキストのdiffアルゴリズムによって生成される、より人間が読める削除および挿入命令とは対照的に、追加、コピー、および実行命令を採用しています。VCDIFFの場合、ここで説明するxdeltaアルゴリズムのようなものが必要です xmailserver.org/xdfs.pdf
asgerhallas

回答:


175

O(ND)差分アルゴリズムとそのバリエーションは素晴らしい論文であり、そこから始めたいと思うかもしれません。これには、疑似コードと、diffの実行に関連するグラフトラバーサルの視覚化が含まれています。

セクション4このペーパーのでは、アルゴリズムを非常に効果的なものにするいくつかの改良点を紹介しています。

これを正常に実装すると、ツールボックスに非常に便利なツールが残されます(おそらくいくつかの優れたエクスペリエンスも得られます)。

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

これは、少しのドキュメント、完全なソースコードを含むページです、前述のアルゴリズムの手法を使用した、およびdiffアルゴリズムの例です。

ソースコード密接に基本的なアルゴリズムに従って表示され、読みやすいです。

入力の準備についても少し役立つので、参考にしてください。文字またはトークン(単語)で比較する場合、出力に大きな違いがあります。

幸運を!


1
リンクがうまくいかない場合、これはMyers 1986です。たとえばciteseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927を参照してください-HuntdiffおよびMcIlroyによるUnix 論文へのリンクも含まれています。
Tripleee、2016年

34

まず、GNUが提供している diffの実際の ソースコードを確認します。

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

基本的なアルゴリズムは、「O(ND)差分アルゴリズムとそのバリエーション」、ユージーンW.マイヤーズ、 'Algorithmica' Vol。1 No. 2、1986、pp。251-266; 「A File Comparison Program」、Webb Miller and Eugene W. Myers、「Software--Practice and Experience」Vol。15 No.11、1985、pp。1025-1040。このアルゴリズムは、「近似文字列マッチングのアルゴリズム」、E。Ukkonen、「情報と制御」、Vol。64、1985、pp.100-118。

論文を読み、実装のソースコードを見るだけで、それがどのように機能するかを理解できます。


80
うーん、要するに、実際のソースコードから根本的なアルゴリズムを理解することは(特に、それが効率的になるように最適化されている場合)、かなり複雑になる可能性があります。プログラムが段階的に行っていることは理解できますが、正確には「なぜ」、またはその概要はわかりません...例:正規表現がどのように機能するか(またはそれらが何であるか)を理解できないPerlの正規表現の実装を調べます。または、それが可能であれば、私は帽子を傾けます。何が起こっているのかを理解するために、より詳細でより高いレベルの概要が必要です。
ダニエル・マグリオラ2009

3
Perlの大部分がどのように機能するかは理解できません:-)ですが、パッケージのドキュメント(更新を参照)にリンクがあり、Perlではなく、diffアルゴリズムであると説明されています。
paxdiablo 2009

32
コードを読まないでください。論文を読んでください。
Ira Baxter

31

https://github.com/google/diff-match-patchご覧ください

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

また、wikipedia.orgの差分ページと「Bram Cohen:差分の問題が解決されました」も参照してください


2
CohenのアルゴリズムはPatience Diffとしても知られているように見えることを述べたかっただけです。これは、バザールの(デフォルト?)diffアルゴリズムであり、gitのオプションのアルゴリズムです。
Dale Hagglund、2010

13

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

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

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]

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.