차이 알고리즘? [닫은]


164

작동하고 효율적인 diff 알고리즘에 대한 설명이 미친 것처럼 보였습니다.

내가 얻은 가장 가까운 것은 RFC 3284 (여러 Eric Sink 블로그 게시물에서)에 대한 링크 이며, diff 결과가 저장되는 데이터 형식 을 완벽하게 이해할 수있는 용어로 설명 합니다. 그러나 diff를 수행하는 동안 프로그램이 이러한 결과에 도달하는 방법에 대해서는 언급하지 않았습니다.

diff 알고리즘을 구현할 때 절충이 있어야한다고 확신하기 때문에 개인적 호기심에서 이것을 연구하려고합니다. 그것 대신에?"...

VCDIFF를 출력하는 효율적인 알고리즘에 대한 설명은 어디에서 찾을 수 있습니까?
그건 그렇고, SourceGear의 DiffMerge에서 사용하는 실제 알고리즘에 대한 설명을 찾으면 훨씬 좋습니다.

참고 : 가장 긴 공통 하위 시퀀스는 VCDIFF에서 사용하는 알고리즘이 아닌 것처럼 보입니다. 사용하는 데이터 형식에 따라 더 똑똑한 것으로 보입니다.


Wikipedia에 아무것도 없습니까? 파이썬과 같은 고급 언어로 다른 구현을 찾으려고 시도 할 수 있습니다 .C 구현보다 이해하기 쉽습니다. 파이썬은 쉽게 읽을 수있는 것으로 유명합니까? 파이썬에는 difflib가 있습니다. 다음은 소스 URL입니다. 소스에는 diff 알고리즘에 대한 수많은 의견이 있습니다. svn.python.org/view/python/trunk/Lib/…
— bsergean 2009

4
RFC는 알고리즘을 설명하기위한 것이 아닙니다. 인터페이스 (/ 프로토콜)를 설명하기위한 것입니다.

2
실제로 가장 일반적인 하위 시퀀스 문제인 diff 알고리즘의 핵심은 Wikipedia에서 찾을 수 있습니다. 이 페이지는 내가 지정은 diff를 쓸 필요로 할 때 내가 도움이 발견 알고리즘 및 샘플 코드의 개요를 제공합니다 en.wikipedia.org/wiki/Longest_common_subsequence_problem
— 코윈 기쁨

3
아마도 이것은 도움이 될 것입니다 : paulbutler.org/archives/a-simple-diff-algorithm-in-php 확실히 훌륭하고 너무 작습니다 ( 전체적으로 29 줄 ; 2 개의 기능이 있습니다). Stack Overflow의 수정 버전 비교와 비슷합니다.
— Nathan

VCDIFF는 사람이 읽을 수있는 diff 용이 아닙니다. 대부분의 일반 텍스트 diff 알고리즘에 의해 생성되는 사람이 읽을 수있는 삭제 및 삽입 명령과 달리 add, copy 및 run 명령을 사용합니다. VCDIFF의 경우 여기에 설명 된 xdelta algortihm과 같은 것이 필요합니다. 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을 참조하십시오. 여기에는 diffHunt와 McIlroy 의 Unix 논문 링크가 포함되어 있습니다.
— tripleee

34

GNU에서 사용할 수 있는 diff 의 실제 소스 코드 를 살펴 보는 것부터 시작하겠습니다 .

해당 소스 코드가 실제로 어떻게 작동하는지 이해하기 위해 해당 패키지의 문서는 소스 코드에서 영감을 얻은 논문을 참조합니다.

기본 알고리즘은 "O (ND) 차이 알고리즘 및 그 변형", Eugene W. Myers, 'Algorithmica'Vol. 1 No. 2, 1986, pp. 251-266; "파일 비교 프로그램"에서 Webb Miller와 Eugene W. Myers, 'Software--Practice and Experience'Vol. 15 No. 11, 1985, 1025-1040 쪽. 알고리즘은 "근사 문자열 매칭 알고리즘", E. Ukkonen,`Information and Control 'Vol. 64, 1985, 100-118 쪽.

논문을 읽고 구현을위한 소스 코드를 살펴보면 작동 방식을 이해하기에 충분해야합니다.


80
간단히 말해 실제 소스 코드에서 기본 알고리즘을 알아내는 경우가 있습니다 (특히 효율적으로 최적화 된 경우). 프로그램이 단계별로 수행하는 작업을 이해할 수는 있지만 정확히 "이유"또는 그에 대한 높은 수준의 개요는 아닙니다 ... 예 : 정규 표현식이 어떻게 작동하는지 (또는 그 작동 방식) 이해하지 못할 것입니다. Perl의 Regexes의 구현을 살펴 보았습니다. 또는 그렇게 할 수 있다면, 나는 모자를 기울이고, 무엇이 일어나고 있는지 파악하기 위해 분명히 더 높은 수준의 개요가 필요합니다.
— Daniel Magliola

3
나는 Perl의 대다수가 어떻게 작동하는지 이해하지 못하지만 패키지 문서 (업데이트 참조)에는 그것을 설명하는 문헌 (Perl이 아닌 diff 알고리즘 임)을 가리키는 링크가 있습니다.
— paxdiablo

32
코드를 읽지 마십시오. 신문을 읽으십시오.
— Ira Baxter

31

https://github.com/google/diff-match-patch를 참조 하십시오

"Diff Match 및 Patch 라이브러리는 일반 텍스트 동기화에 필요한 작업을 수행하는 강력한 알고리즘을 제공합니다. ... 현재 Java, JavaScript, C ++, C # 및 Python에서 사용 가능"

또한 wikipedia.org Diff 페이지 와 " Bram Cohen : diff 문제가 해결되었습니다 "를 참조하십시오.


2
코헨의 알고리즘은 인내심 차이라고도합니다. 시장의 (기본?) diff 알고리즘이고 git의 선택적 알고리즘입니다.
— Dale Hagglund

13

diff 알고리즘을 찾고 여기에 와서 나만의 구현을했습니다. vcdiff에 대해 모르겠습니다.

위키 백과 : 가장 긴 공통 서브 시퀀스에서 diff-like 출력을 얻는 작은 단계 일뿐입니다. 서브 시퀀스에 항목이 없지만 원래 항목에 있으면 항목을 삭제해야합니다. (아래의 '–'표시) 하위 시퀀스에는 없지만 두 번째 시퀀스에있는 경우 추가해야합니다 ( '+'표시).

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.