Алгоритм Diff

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

Ближайший я получил эту ссылку для RFC 3284 (из нескольких сообщений в блоге Eric Sink), которая в совершенно понятных выражениях описывает формат данных в котором хранятся результаты разностного анализа. Тем не менее, он не имеет никакого упоминания о том, как программа будет достигать этих результатов при выполнении diff.

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

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

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

Спасибо!

Ответ 1

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

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

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

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

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

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

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

Удачи!

Ответ 2

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

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

Базовый алгоритм описан в "Алгоритме разности давлений (ND) и его вариациях", Евгений У. Майерс, "Алгоритмика" Vol. 1 № 2, 1986, стр. 251-266; и в "Файл" "Сравнительная программа", Уэбб Миллер и Юджин В. Майерс, "Программное обеспечение - практика и опыт", том 15 № 11, 1985, стр. 1025-1040. Алгоритм был независимо обнаружен, как описано в "Алгоритмы приближенного соответствия строк", Е. Укконен, "Информация и управление", том 64, 1985, стр. 100-118.

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

Ответ 3

См. http://code.google.com/p/google-diff-match-patch/

"Библиотеки Diff Match и Patch предлагают надежные алгоритмы для выполнения операции, необходимые для синхронизации простой текст.... Доступен на данный момент в Java, JavaScript, С++, С# и Python"

Также см. wikipedia.org Diff page и - " Брэм Коэн: Проблема diff была решена.

Ответ 4

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

Wikipedia: от самой длинной общей подпоследовательности это всего лишь небольшой шаг для получения результата с разностным отображением: если элемент отсутствует в подпоследовательность, но присутствующая в оригинале, она должна быть удалена. (Знаки "-" ниже). Если он отсутствует в подпоследовательности, но присутствует во второй последовательности, он должен быть добавлен. (Знаки "+".)

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

Ссылка на быструю реализацию Rubis rubis здесь.

Моя медленная и простая рубиновая адаптация ниже.

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]

Ответ 5

Основываясь на ссылке, которую дал Эммелаич, также существует большой пробег Diff Strategies на веб-сайт Нила Фрейзера (один из авторов библиотеки).

Он охватывает основные стратегии и в конце статьи продвигается к алгоритму Майера и некоторой теории графов.