อัลกอริทึม Diff? [ปิด]


164

ฉันดูบ้าไปแล้วสำหรับคำอธิบายของอัลกอริธึม diff ที่ใช้งานได้และมีประสิทธิภาพ

ที่ใกล้เคียงที่สุดที่ฉันได้รับคือลิงก์ไปยัง RFC 3284 (จากหลายบทความของ Eric Sink บล็อก) ซึ่งอธิบายในแง่ที่เข้าใจได้อย่างสมบูรณ์แบบรูปแบบข้อมูลที่เก็บผลลัพธ์ต่าง ๆ อย่างไรก็ตามไม่ได้กล่าวถึงว่าโปรแกรมจะบรรลุผลลัพธ์เหล่านี้อย่างไรในขณะที่ทำโปรแกรม

ฉันพยายามค้นคว้าเรื่องความอยากรู้อยากเห็นแบบส่วนตัวเพราะฉันแน่ใจว่าจะต้องมีการแลกเปลี่ยนเมื่อใช้อัลกอริธึม diff ซึ่งค่อนข้างชัดเจนในบางครั้งเมื่อคุณดู diffs และสงสัยว่า "ทำไมโปรแกรม diff จึงเลือกสิ่งนี้เป็นการเปลี่ยนแปลง แทนที่จะเป็นเช่นนั้น "...

ฉันจะหาคำอธิบายเกี่ยวกับอัลกอริทึมที่มีประสิทธิภาพที่จะสิ้นสุดการแสดงผล VCDIFF ได้อย่างไร
อย่างไรก็ตามถ้าคุณพบคำอธิบายของอัลกอริทึมจริงที่ใช้โดย DiffMerge ของ SourceGear นั่นจะดีกว่า

หมายเหตุ: การเรียงลำดับทั่วไปที่ยาวที่สุดดูเหมือนจะไม่ได้เป็นอัลกอริธึมที่ VCDIFF ใช้ดูเหมือนว่าพวกเขากำลังทำอะไรที่ฉลาดกว่าโดยใช้รูปแบบข้อมูลที่ใช้


ไม่มีอะไรในวิกิพีเดีย? คุณอาจลองค้นหาการนำไปใช้อื่นในระดับสูงเช่น python ซึ่งอาจเข้าใจได้ง่ายกว่าการใช้ C Python มีชื่อเสียงในเรื่องการอ่านง่าย? งูหลามนั้นมี difflib นี่คือ URL ของแหล่งที่มา แหล่งที่มามีความคิดเห็นมากมายเกี่ยวกับอัลกอริทึมต่าง svn.python.org/view/python/trunk/Lib/…
bsergean

4
RFC ไม่ได้มีไว้เพื่ออธิบายอัลกอริทึม มีไว้เพื่ออธิบายอินเตอร์เฟส (/ โปรโตคอล)

2
ที่จริงแล้วแกนกลางของอัลกอริทึม diff ซึ่งเป็นปัญหาลำดับย่อยที่ยาวที่สุดสามารถพบได้ใน Wikipedia หน้านี้ให้ภาพรวมของอัลกอริทึมและโค้ดตัวอย่างที่ฉันพบว่ามีประโยชน์เมื่อฉันต้องการเขียน 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) และการเปลี่ยนแปลงของมันเป็นกระดาษที่ยอดเยี่ยมและคุณอาจต้องการเริ่มต้นที่นั่น มันมีรหัสหลอกและการสร้างภาพที่ดีของ traversals กราฟที่เกี่ยวข้องในการทำ diff

ส่วนที่ 4ของบทความแนะนำการปรับแต่งบางอย่างสำหรับอัลกอริทึมที่ทำให้มีประสิทธิภาพมาก

การติดตั้งสำเร็จจะทำให้คุณมีเครื่องมือที่มีประโยชน์มากในกล่องเครื่องมือของคุณ (และอาจมีประสบการณ์ที่ยอดเยี่ยมเช่นกัน)

การสร้างรูปแบบผลลัพธ์ที่คุณต้องการในบางครั้งอาจเป็นเรื่องยุ่งยาก แต่ถ้าคุณมีความเข้าใจในขั้นตอนวิธีภายในคุณควรจะสามารถส่งออกสิ่งที่คุณต้องการ นอกจากนี้คุณยังสามารถแนะนำการวิเคราะห์พฤติกรรมเพื่อส่งผลต่อผลผลิตและทำการแลกเปลี่ยนบางอย่าง

นี่คือหน้าเว็บที่มีเอกสารประกอบเล็กน้อยซอร์สโค้ดแบบเต็มและตัวอย่างอัลกอริธึม diff โดยใช้เทคนิคในอัลกอริทึมดังกล่าวข้างต้น

รหัสที่มาดูเหมือนจะเป็นไปตามขั้นตอนวิธีการขั้นพื้นฐานอย่างใกล้ชิดและเป็นเรื่องง่ายที่จะอ่าน

นอกจากนี้ยังมีการเตรียมการป้อนข้อมูลเล็กน้อยซึ่งคุณอาจพบว่ามีประโยชน์ มีความแตกต่างอย่างมากในเอาต์พุตเมื่อคุณกระจายตัวละครหรือโทเค็น (คำ)

โชคดี!


1
ในกรณีที่ลิงก์ไม่ดีนี่คือ Myers 1986; ดูเช่นciteseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - นอกจากนี้ยังมีลิงก์ไปยังdiffกระดาษUnix ของ Hunt และ McIlroy
tripleee

34

ฉันจะเริ่มต้นด้วยการมองหาที่เกิดขึ้นจริง รหัสที่มาสำหรับ diff ซึ่ง GNU ทำให้สามารถใช้ได้

เพื่อให้เข้าใจถึงวิธีการทำงานของซอร์สโค้ดจริงเอกสารในแพ็คเกจนั้นอ้างอิงเอกสารที่เป็นแรงบันดาลใจ:

อัลกอริทึมพื้นฐานอธิบายไว้ใน "อัลกอริทึมความแตกต่าง O (ND) และการเปลี่ยนแปลง", Eugene W. Myers, 'Algorithmica' Vol. 1 หมายเลข 2, 1986, pp. 251-266; และใน "โปรแกรมเปรียบเทียบไฟล์", Webb Miller และ Eugene W. Myers, 'ซอฟต์แวร์ - การปฏิบัติและประสบการณ์' ฉบับที่ 15 หมายเลข 11, 1985, pp. 1025-1040 อัลกอริทึมถูกค้นพบอย่างอิสระตามที่อธิบายไว้ใน "อัลกอริทึมสำหรับการจับคู่สตริงโดยประมาณ", E. Ukkonen, `ข้อมูลและการควบคุม 'ฉบับ 64, 1985, pp. 100-118

การอ่านเอกสารแล้วดูที่ซอร์สโค้ดสำหรับการติดตั้งควรจะมากเกินพอที่จะเข้าใจวิธีการทำงาน


80
อืมในบางครั้งการหาอัลกอริธึมพื้นฐานจากซอร์สโค้ดจริง (โดยเฉพาะอย่างยิ่งถ้ามันได้รับการปรับปรุงให้มีประสิทธิภาพ) นั้นค่อนข้างซับซ้อน ฉันจะสามารถเข้าใจสิ่งที่โปรแกรมกำลังทำทีละขั้นตอน แต่ไม่ใช่ "ทำไม" หรือภาพรวมระดับสูงเกี่ยวกับเรื่องนั้น ... ตัวอย่าง: คุณไม่เคยเข้าใจว่านิพจน์ทั่วไปทำงานอย่างไร ดูที่การใช้งาน Regexes ของ Perl หรือถ้าคุณสามารถทำสิ่งนั้นได้ฉันก็จะให้ทิปกับหมวกฉันต้องอธิบายภาพรวมในระดับที่สูงขึ้นเพื่อให้เข้าใจว่าเกิดอะไรขึ้น
Daniel Magliola

3
ฉันไม่เคยเข้าใจว่า Perl ส่วนใหญ่ทำงานอย่างไร :-) แต่มีลิงก์ในเอกสารแพคเกจ (ดูอัปเดต) ที่จะชี้ให้คุณไปที่เอกสารที่อธิบายถึงมัน (เป็นอัลกอริทึม diff ไม่ใช่ Perl)
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
แค่อยากจะพูดถึงว่าอัลกอริทึมของโคเฮนก็ดูเหมือนจะเป็นที่รู้จักในนาม Patience Diff มันคืออัลกอริธึม diff (เป็นค่าเริ่มต้น) ในตลาดสดและอีกทางเลือกหนึ่งในคอมไพล์
Dale Hagglund

13

ฉันมาที่นี่เพื่อค้นหาอัลกอริธึม diff และหลังจากนั้นก็ทำให้การใช้งานของฉันเอง ขอโทษฉันไม่รู้เกี่ยวกับ 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

ขึ้นอยู่กับการเชื่อมโยง Emmelaich ให้นอกจากนี้ยังมีการทำงานที่ลดลงที่ดีของกลยุทธ์ Diff บนเว็บไซต์ของนีลเฟรเซอร์ (หนึ่งในผู้เขียนของห้องสมุด)

เขาครอบคลุมกลยุทธ์พื้นฐานและในตอนท้ายของบทความจะดำเนินการตามอัลกอริทึมของ Myer และทฤษฎีกราฟบางอย่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.