차이 알고리즘? [닫은]


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.