Algoritmo diff? [chiuso]


164

Sono sembrato pazzo per una spiegazione di un algoritmo diff che funziona ed è efficiente.

Il più vicino che ho ottenuto è questo link a RFC 3284 (da diversi post sul blog di Eric Sink), che descrive in termini perfettamente comprensibili il formato dei dati in cui sono memorizzati i risultati del diff. Tuttavia, non ha alcun riferimento al modo in cui un programma raggiungerebbe questi risultati facendo un diff.

Sto cercando di ricercare questo per curiosità personale, perché sono sicuro che ci devono essere dei compromessi quando si implementa un algoritmo diff, che sono abbastanza chiari a volte quando si guardano le differenze e ci si chiede "perché il programma diff ha scelto questo come un cambiamento al posto di quello?"...

Dove posso trovare una descrizione di un algoritmo efficiente che finirebbe per produrre VCDIFF?
A proposito, se ti capita di trovare una descrizione dell'algoritmo effettivo utilizzato da DiffMerge di SourceGear, sarebbe ancora meglio.

NOTA: la sottosequenza comune più lunga non sembra essere l'algoritmo utilizzato da VCDIFF, sembra che stiano facendo qualcosa di più intelligente, dato il formato dei dati che usano.


Niente su Wikipedia? Puoi forse provare a trovare un'altra implementazione in una lingua di alto livello come Python, che potrebbe essere più facile da capire di un'implementazione C. Python è famoso per essere facilmente leggibile? C'è un difflib in Python. Ecco l'URL alla fonte. La fonte ha tonnellate di commenti sugli algoritmi diff. svn.python.org/view/python/trunk/Lib/…
bsergean

4
Gli RFC non intendono descrivere algoritmi. Hanno lo scopo di descrivere interfacce (/ protocolli).

2
In realtà, il nocciolo dell'algoritmo diff, il più lungo problema comune della sotto-sequenza, può essere trovato su Wikipedia. Questa pagina offre una panoramica dell'algoritmo e del codice di esempio che ho trovato utile quando avevo bisogno di scrivere un diff personalizzato: en.wikipedia.org/wiki/Longest_common_subsequence_problem
Corwin Joy

3
Forse questo aiuterà: paulbutler.org/archives/a-simple-diff-algorithm-in-php È sicuramente fantastico, ed è così piccolo (solo 29 righe in tutto ; ha 2 funzioni). È simile alla cosa di confronto di revisione delle modifiche di Stack Overflow.
Nathan,

VCDIFF non è per differenze leggibili dall'uomo. Impiega le istruzioni di aggiunta, copia ed esecuzione in contrapposizione alle istruzioni di eliminazione e inserimento più leggibili dall'uomo emesse dalla maggior parte degli algoritmi diff di testo semplice. Per VCDIFF hai bisogno di qualcosa come xdelta algortihm
asgerhallas

Risposte:


175

Un algoritmo di differenza O (ND) e le sue variazioni è un documento fantastico e potresti voler iniziare da lì. Include pseudo-codice e una bella visualizzazione dei traversi del grafico coinvolti nel fare il diff.

La sezione 4 dell'articolo introduce alcuni perfezionamenti dell'algoritmo che lo rendono molto efficace.

L'implementazione riuscita di questo ti lascerà uno strumento molto utile nella tua cassetta degli attrezzi (e probabilmente anche un'esperienza eccellente).

Generare il formato di output di cui hai bisogno a volte può essere complicato, ma se hai comprensione degli algoritmi interni, dovresti essere in grado di produrre tutto ciò di cui hai bisogno. È inoltre possibile introdurre l'euristica per influire sulla produzione e apportare alcuni compromessi.

Ecco una pagina che include un po 'di documentazione, codice sorgente completo ed esempi di un algoritmo diff che utilizza le tecniche dell'algoritmo sopra menzionato.

Il codice sorgente sembra seguire da vicino l'algoritmo di base ed è facile da leggere.

C'è anche un po 'di preparazione dell'input, che potresti trovare utile. C'è un'enorme differenza nell'output quando si differenzia per carattere o token (parola).

In bocca al lupo!


1
Nel caso in cui il collegamento non funzioni, questo è Myers 1986; vedere ad esempio citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - include inoltre un collegamento al diffdocumento Unix di Hunt e McIlroy.
Tripleee

34

Vorrei iniziare guardando il codice sorgente effettivo per diff, che GNU rende disponibile .

Per capire come funziona effettivamente quel codice sorgente, i documenti in quel pacchetto fanno riferimento ai documenti che lo hanno ispirato:

L'algoritmo di base è descritto in "An O (ND) Difference Algorithm and its Variations", Eugene W. Myers, 'Algorithmica' Vol. 1 n. 2, 1986, pagg. 251-266; e in "Un programma di confronto di file", Webb Miller ed Eugene W. Myers, "Software - Practice and Experience" Vol. 15 n. 11, 1985, pagg. 1025-1040. L'algoritmo è stato scoperto in modo indipendente come descritto in "Algorithms for Approximate String Matching", E. Ukkonen, "Information and Control" Vol. 64, 1985, pagg. 100-118.

Leggere i documenti e guardare il codice sorgente per un'implementazione dovrebbe essere più che sufficiente per capire come funziona.


80
Hmmm, in breve, a volte capire l'algoritmo sottostante dal codice sorgente effettivo (specialmente se è ottimizzato per essere efficiente) può essere abbastanza complesso. Sarò in grado di capire cosa sta facendo il programma passo dopo passo, ma non esattamente "perché", o una panoramica di alto livello su questo ... Esempio: Non capiresti mai come funzionano le espressioni regolari (o cosa sono) di esaminando l'implementazione dei Regex di Perl. O se potessi farlo, allora mi metto il cappello, ho sicuramente bisogno di una panoramica più spiegata, di livello superiore per capire cosa sta succedendo.
Daniel Magliola,

3
Non capisco mai come funzioni la stragrande maggioranza di Perl :-), ma c'è un link nei documenti del pacchetto (vedi aggiornamento) che ti indicherà la letteratura che lo descrive (essendo l'algoritmo diff, non Perl).
paxdiablo,

32
Non leggere il codice Leggi il documento.
Ira Baxter,

31

Vedi https://github.com/google/diff-match-patch

"Le librerie Diff Match e Patch offrono algoritmi robusti per eseguire le operazioni richieste per la sincronizzazione del testo normale. ... Attualmente disponibile in Java, JavaScript, C ++, C # e Python"

Vedi anche la pagina Diff di wikipedia.org e - " Bram Cohen: il problema diff è stato risolto "


2
Volevo solo ricordare che l'algoritmo di Cohen sembra anche essere conosciuto come Diff di pazienza. È l'algoritmo diff (predefinito?) In bazaar e uno opzionale in git.
Dale Hagglund,

13

Sono venuto qui alla ricerca dell'algoritmo diff e successivamente ho realizzato la mia implementazione. Mi dispiace non so di vcdiff.

Wikipedia : da una sottosequenza comune più lunga è solo un piccolo passo per ottenere un output simile a diff: se un elemento è assente nella sottosequenza ma presente nell'originale, deve essere stato eliminato. (I segni "-", sotto.) Se è assente nella sottosequenza ma presente nella seconda sequenza, deve essere stato aggiunto. (I segni "+").

Bella animazione dell'algoritmo LCS qui .

Link a un'implementazione ruby ​​LCS veloce qui .

Il mio adattamento rubino lento e semplice è sotto.

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]

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.