Дифференциальный алгоритм? [закрыто]

164

Я выгляжу как сумасшедший для объяснения алгоритма сравнения, который работает и эффективен.

Самое близкое, что я получил, - это ссылка на RFC 3284 (из нескольких сообщений в блоге Эрика Синка), которая в понятной форме описывает формат данных, в котором хранятся результаты сравнения. Тем не менее, в нем ничего не сказано о том, как программа достигла бы этих результатов при выполнении различий.

Я пытаюсь исследовать это из личного любопытства, потому что я уверен, что при реализации алгоритма diff должны быть компромиссы, которые иногда довольно понятны, когда вы смотрите на diff и задаетесь вопросом: «Почему программа diff выбрала это как изменение? вместо этого?"...

Где я могу найти описание эффективного алгоритма, который в конечном итоге выведет VCDIFF?
Кстати, если вы обнаружите описание фактического алгоритма, используемого DiffMerge SourceGear, это было бы еще лучше.

ПРИМЕЧАНИЕ: самая длинная общая подпоследовательность, кажется, не является алгоритмом, используемым VCDIFF, похоже, что они делают что-то более умное, учитывая формат данных, который они используют.

Даниэль Маглиола
источник
Ничего в википедии? Вы можете попытаться найти другую реализацию в языке высокого уровня, такую ​​как python, которая может быть легче для понимания, чем реализация на C. Python известен тем, что легко читается? В питоне есть различие Вот ссылка на источник. Источник содержит множество комментариев о алгоритмах сравнения. svn.python.org/view/python/trunk/Lib/...
bsergean
4
RFC не предназначены для описания алгоритмов. Они предназначены для описания интерфейсов (/ протоколов).
2
На самом деле, ядро ​​алгоритма diff, самой длинной общей проблемы подпоследовательности, можно найти в Википедии. На этой странице представлен обзор алгоритма и примера кода, которые я нашел полезными, когда мне нужно было написать собственный 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 algortihm, описанного здесь xmailserver.org/xdfs.pdf
asgerhallas

Ответы:

175

Разностный алгоритм O (ND) и его вариации - фантастическая статья, и вы можете начать с нее. Он включает в себя псевдокод и красивую визуализацию обхода графа, участвующего в выполнении diff.

Раздел 4 статьи представляет некоторые усовершенствования алгоритма, которые делают его очень эффективным.

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

Генерирование выходного формата, которое вам нужно, иногда может быть сложным, но если вы разбираетесь во внутренних принципах алгоритма, тогда вы сможете вывести все, что вам нужно. Вы также можете ввести эвристику, чтобы повлиять на выход и сделать определенные компромиссы.

Вот страница, которая включает немного документации, полный исходный код и примеры алгоритма сравнения с использованием методов в вышеупомянутом алгоритме.

Исходный код , как представляется , внимательно следить за основной алгоритм и легко читается.

Также есть немного о подготовке входных данных, которые могут оказаться полезными. Существует огромная разница в выводе, когда вы выполняете различие по символу или токену (слову).

Удачи!

jscharf
источник
1
На случай, если связь испортится, это Myers 1986; см., например, citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - здесь также есть ссылка на diffстатью Unix Ханта и Макилроя.
tripleee
34

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

Для понимания того, как на самом деле работает этот исходный код, документы в этом пакете ссылаются на статьи, которые его вдохновили:

Основной алгоритм описан в «Разностный алгоритм O (ND) и его вариации», Юджин В. Майерс, «Алгоритмика», вып. 1 № 2, 1986, с. 251-266; и в «Программе сравнения файлов», Уэбб Миллер и Юджин В. Майерс, «Программное обеспечение - практика и опыт», том. 15 № 11, 1985, с. 1025-1040. Алгоритм был открыт независимо, как описано в «Алгоритмы для приближенного сопоставления строк», Э. Укконен, «Информация и управление», вып. 64, 1985, с. 100-118.

Чтение статей, а затем поиск исходного кода для реализации должно быть более чем достаточно, чтобы понять, как это работает.

paxdiablo
источник
80
Хм, короче говоря, иногда выяснение базового алгоритма из реального исходного кода (особенно если он оптимизирован для повышения эффективности) может быть довольно сложным. Я буду в состоянии понять, что программа делает шаг за шагом, но не совсем «почему», или общий обзор об этом ... Пример: вы никогда не поймете, как работают регулярные выражения (или что они), глядя на реализацию регулярных выражений Perl. Или, если вы можете сделать это, тогда я снимаю шляпу, мне определенно нужен более подробный обзор более высокого уровня, чтобы выяснить, что происходит.
Даниэль Маглиола
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
Просто хотел упомянуть, что алгоритм Коэна, похоже, также известен как Patience Diff. Это (по умолчанию?) Алгоритм сравнения на базаре и необязательный в git.
Дейл Хэгглунд
13

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

Википедия : Из самой длинной общей подпоследовательности это всего лишь маленький шаг для получения различий в выводе: если элемент отсутствует в подпоследовательности, но присутствует в оригинале, он должен быть удален. (Отметки «-» ниже.) Если он отсутствует в подпоследовательности, но присутствует во второй последовательности, он должен быть добавлен в (отметки «+».)

Хорошая анимация алгоритма LCS здесь .

Ссылка на быструю реализацию LCS 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]
neoneye
источник