Алгоритм різниці? [зачинено]


164

Я розглядав як божевільний пояснення розрізного алгоритму, який працює і є ефективним.

Найближче мені це посилання на RFC 3284 (з декількох публікацій блогу Еріка Сінка), в якому абсолютно ідеально описується формат даних, у якому зберігаються різні результати. Однак у ній немає жодної згадки про те, як програма досягає цих результатів, роблячи різні.

Я намагаюся дослідити це з особистої цікавості, тому що я впевнений, що повинні бути компроміси при впровадженні алгоритму diff, які досить зрозумілі, коли ви дивитесь на diff і дивуєтесь: "чому програма diff вибрала це як зміну замість цього? "...

Де я можу знайти опис ефективного алгоритму, який у кінцевому підсумку виводить VCDIFF?
До речі, якщо вам трапиться опис фактичного алгоритму, використовуваного DiffMerge SourceGear, це було б ще краще.

ПРИМІТКА: найдовша загальна підпорядкованість не здається алгоритмом, який використовується VCDIFF, схоже, вони роблять щось розумніше, враховуючи формат даних, який вони використовують.


Нічого у вікіпедії? Можливо, ви можете спробувати знайти іншу реалізацію у високому рівні, наприклад, python, що може бути легше зрозуміти, ніж реалізація C. Пітон відомий тим, що легко читається? Існує дифліб пітона. Ось URL-адреса до джерела. У джерелі є багато коментарів щодо алгоритмів розходження. svn.python.org/view/python/trunk/Lib/…
bsergean

4
RFC не призначені для опису алгоритмів. Вони призначені для опису інтерфейсів (/ протоколів).

2
Власне, ядро ​​алгоритму diff, найдовшої поширеної проблеми підпорядку, можна знайти у Вікіпедії. На цій сторінці ви знайдете огляд алгоритму та зразкового коду, який мені здався корисним, коли мені потрібно було написати спеціальний розріз: en.wikipedia.org/wiki/Longest_common_subsequence_problem
Corwin Joy,

3
Можливо, це допоможе: paulbutler.org/archives/a-simple-diff-algorithm-in-php Це, безумовно, приголомшливо, і це так мало (всього 29 рядків ; усього це 2 функції). Це схоже на порівняння редакції Stack Overflow.
Натан

VCDIFF не для людей, що читають, відрізняється. Тут використовуються інструкції додавання, копіювання та запуску на відміну від більш зрозумілих для людей видалення та вставки інструкцій, що випромінюються більшістю алгоритмів простого тексту. Для VCDIFF вам потрібно щось на зразок алгоритму xdelta, описаного тут xmailserver.org/xdfs.pdf
asgerhallas

Відповіді:


175

Алгоритм різниці O (ND) та його варіації - це фантастичний документ, і ви, можливо, захочете почати саме там. Він включає псевдокод і гарну візуалізацію трафіку графіків, які беруть участь у виконанні різниці.

Розділ 4 статті вводить деякі уточнення в алгоритм, які роблять його дуже ефективним.

Успішно реалізуючи це, ви отримаєте дуже корисний інструмент у вашій панелі інструментів (і, мабуть, також відмінний досвід).

Генерування потрібного вам вихідного формату іноді може бути складним, але якщо ви розумієте внутрішні алгоритми, то ви повинні мати можливість вивести все необхідне. Ви також можете ввести евристику, щоб вплинути на обсяг виробництва та здійснити певні компроміси.

Ось сторінка, яка містить трохи документації, повний вихідний код та приклади алгоритму diff з використанням методів у вищезгаданому алгоритмі.

Вихідний код , як видається , уважно стежити за основний алгоритм і легко читається.

Існує також трохи про підготовку вводу, який може бути корисним. Існує величезна різниця у виході, коли ви відрізняєтесь за символом чи символом (словом).

Удачі!


1
Якщо посилання погано походить, це Майерс 1986; дивись , наприклад , citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - це додатково включає в себе посилання на Unix diffроботі Ханта і Макілрой.
тріплей

34

Я б почав з перегляду фактичного вихідного коду для diff, який GNU робить доступним .

Щоб зрозуміти, як насправді працює цей вихідний код, документи в цьому пакеті посилаються на документи, які його надихнули:

Основний алгоритм описаний у "Алгоритмі різниці O (ND) та його варіаціях", Євген У. Майерс, "Algorithmica" Vol. 1 № 2, 1986, с. 251-266; і в "Програмі порівняння файлів", Вебл Міллер та Євген У. Майєрс, "Програмне забезпечення - практика та досвід" Vol. 15 № 11, 1985, С. 1025-1040. Алгоритм був незалежно розкритий, як описано в "Алгоритмах приблизного узгодження рядків", Е. Укконен, "Інформація та управління" Vol. 64, 1985, стор 100-118.

Читання статей, а потім перегляд вихідного коду для реалізації має бути більш ніж достатньо, щоб зрозуміти, як воно працює.


80
Хммм, коротше кажучи, іноді з'ясування основного алгоритму з фактичного вихідного коду (особливо якщо він оптимізований для ефективності) може бути досить складним. Я зможу зрозуміти, що програма робить крок за кроком, але не точно "чому", або огляд високого рівня про це ... Приклад: Ви ніколи не зрозумієте, як працюють регулярні вирази (або що вони є) дивлячись на реалізацію реджексів Перла. Або якщо ви могли це зробити, то я накидаю шапку, мені, безумовно, потрібен більш детальний огляд вищого рівня, щоб зрозуміти, що відбувається.
Даніель Магліола

3
Я ніколи не розумію, як працює переважна більшість Perl :-), але в документі пакету (див. Оновлення) є посилання, яке вказуватиме на літературу, що описує його (це алгоритм diff, а не Perl).
paxdiablo

32
Не читайте код. Прочитайте папір.
Іра Бакстер

31

Дивіться https://github.com/google/diff-match-patch

"Бібліотеки Diff Match і Patch пропонують надійні алгоритми для виконання операцій, необхідних для синхронізації простого тексту. На даний момент доступні в Java, JavaScript, C ++, C # і Python"

Також дивіться сторінку Diff на wikipedia.org і - " Брам Коен: Рішення проблеми вирішено "


2
Просто хотілося б зазначити, що алгоритм Коена також здається відомим як терпіння різниці. Це алгоритм відмінності (за замовчуванням?) У базарі та необов'язковий у git.
Дейл Хагглунд

13

Я прийшов сюди шукати алгоритм різниці і згодом зробив власну реалізацію. Вибачте, я не знаю про vcdiff.

Вікіпедія : З найдовшої загальної підданості це лише невеликий крок, щоб отримати різний вихід: якщо елемент відсутній у підпорядкуванні, але присутній в оригіналі, його потрібно було видалити. (Познаки '-', внизу.) Якщо він відсутній у підрядці, але присутній у другій послідовності, він повинен бути доданий. (Знаки '+'.)

Приємна анімація алгоритму 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]

10

На підставі посилання, яке дав Еммелайх, також існує велика кількість стратегій різниці на веб-сайті Ніла Фрейзера (одного з авторів бібліотеки) .

Він висвітлює основні стратегії і наприкінці статті переходить до алгоритму Мієра та деякої теорії графіків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.