קוד האפמן

מתוך ויקיפדיה, האנציקלופדיה החופשית
קפיצה אל: ניווט, חיפוש
עץ האפמן שנוצר על פי התדירויות במשפט "this is an example of a huffman tree"

קוד האפמן הוא שיטה לקידוד סימנים, כגון תווי טקסט, ללא אובדן נתונים. הקוד שייך למשפחה שימושית של קודים המכונה קודי תחיליות (ראו למטה), ובמשפחה זו הוא הקוד המספק דחיסת נתונים מרבית, כלומר מאחסן את הסימנים במספר מזערי של סיביות, על פי הקריטריון המפורט למטה. השיטה מתבססת על אורך משתנה לסימנים על פי שכיחותם, כך שסימן נפוץ יוצג באמצעות מספר קטן של סיביות. לרוב ניתן לחסוך באמצעות שיטה זו בין 20% ל-90% משטח האחסון. קוד האפמן הוא גרסה כללית יותר של עקרון קידוד הנקרא "קידוד אנטרופיה".

גילוי הקוד[עריכת קוד מקור | עריכה]

בשנת 1951, במסגרת לימודיו לדוקטורט ב־MIT, דייוויד האפמן למד קורס בתורת האינפורמציה שהייתה אז בחיתוליה. המרצה בקורס רוברט פאנו הציע לתלמידים לבחור בין עבודה מסכמת ובין מבחן מסכם. העבודה שהציע פאנו הייתה בנושא מציאת הקוד הבינארי היעיל ביותר - בעיה שהייתה פתוחה באותו הזמן; הקוד הטוב ביותר שהיה ידוע נקרא קוד שאנון-פאנו. האפמן, שלא הצליח להוכיח שאף אחד מהקודים הקיימים הוא יעיל ביותר, עמד להיכנע ולהתחיל ללמוד לבחינה המסכמת, כאשר עלה במוחו הרעיון להשתמש בעצים בינאריים ממויינים על פי תדירות האותיות, כשבנייתם היא מהעלים כלפי מעלה. במהרה הוכיח כי שיטתו היא היעילה ביותר. האפמן עקף את המגבלה המרכזית בקוד שאנון-פאנו על ידי כך שבנה את העץ מלמטה למעלה, ולא ההפך.

רקע[עריכת קוד מקור | עריכה]

במערכות סימנים מקובלות, כגון קוד ASCII, ניתן אורך אחיד לכל הסימנים במערכת. זו שיטה פשוטה, אך היא אינה יעילה מבחינת נפח האחסון שהיא צורכת: יש סימנים נדירים, להם יכולנו לתת קוד ארוך יותר, ולעומתם להעדיף קוד קצר לסימנים הנפוצים. יישום של גישה זו קיים כבר בקוד מורס לטלגרף אלחוטי. לאות הנפוצה 'ו' משמש בקוד מורס הקוד . ("נקודה"), ואילו לאות 'ע' משמש הקוד - - - . ("קו-קו-קו-נקודה").

נמחיש את יעילותה של גישה זו באמצעות דוגמה פשוטה. נניח שנתונה מחרוזת בת 200 תווים, שמחציתם האות א. בקוד ASCII, שבו לכל תו מוקדשות 8 סיביות, אורכה של מחרוזת זו הוא 1,600 סיביות. נקודד כעת את התווים בדרך חדשה: האות א תסומן בסיבית יחידה שערכה 0, וכל תו אחר יסומן בקוד ה-ASCII הרגיל שלו, שבתחילתו תתוסף הסיבית 1 (תוספת זו הכרחית, כדי שנוכל להבדיל בין סיבית 0 שמציינת את האות א, ובין סיבית 0 באמצע תו כלשהו). אורך המחרוזת בקוד זה הוא 1,000 סיביות בלבד, שהם 63% מאורך המחרוזת המקורית.

תיאור פורמלי[עריכת קוד מקור | עריכה]

תיאור הקוד[עריכת קוד מקור | עריכה]

קוד האפמן הוא קוד תחיליות, כלומר כל מחרוזת ביטים שמייצגת אות אינה תחילית של מחרוזת המייצגת אות אחרת. קוד כזה מבטיח אפשרות יחידה לפיענוח, ויתירה מזאת, הפיענוח מהיר, שכן מספיק לעבור על המסמך המקודד פעם אחת מההתחלה ועד הסוף תוך שמירת מעט מידע.

קוד מסוג זה ניתן לייצג על ידי עץ בינארי, כאשר עלי העץ מייצגים את האותיות המקודדות, וצמתי העץ מסומנים ב-0 או 1. כאשר רוצים לפענח רצף ביטים כלשהו, הולכים על העץ על פי הביטים שנקלטים עד אשר מגיעים לעלה. האות המאוחסנת בעלה היא האות המפוענחת.

כאשר הקוד הוא אופטימלי, העץ יהיה עץ מלא. כלומר, לכל צומת שאינו עלה יהיו בדיוק שני בנים. לדוגמה, נניח כי בקוד כלשהו לשורש של העץ יש רק בן אחד, שאליו מובילה הקשת 0. פירוש הדבר הוא שבקוד לא יהיו כלל אותיות שקידודן מתחיל ב-1, וזהו כמובן בזבוז של מקום (כי אפשר היה, לכל הפחות, לתת לאות כלשהי את הקידוד "1", ובכך לחסוך במקום).

תיאור הבעיה[עריכת קוד מקור | עריכה]

הבעיה העיקרית שאותה פתר האפמן אינה תיאור הקוד עצמו, אלא מציאת אלגוריתם יעיל שמבטיח בנייה של קוד כזה בהינתן מסמך כלשהו. מרגע שקיים קוד כנדרש, קידוד ופענוח ניתנים לביצוע במעבר אחד על הטקסט, ולכן הבעיה הגדולה היא מציאת הקוד. מכיוון שאופטימליות הקוד מתבססת על מספר המופעים של כל אות בטקסט, לא קיים קוד אחד שנותן קידוד אופטימלי לכל טקסט, ולכן נדרשת הפעלה של האלגוריתם ובניית הקוד המתאים עבור טקסט ספציפי.

מבחינה פורמלית, הקלט לבעיה הוא אוסף אותיות, \ a_1,a_2,\dots,a_n, ומספר הפעמים שכל אות מופיעה בטקסט: \ c_1,c_2,\dots,c_n.

הפלט הצפוי הוא קוד, שהוא אוסף \ h_1,h_2,\dots,h_n של מילים בינאריות. נסמן \ |h_k| את אורכה של המילה ה-\ k.

היעד הוא שהקוד שיוחזר יהיה כזה כך ש- \ \sum_{k=1}^n c_k\cdot|h_k| יהיה מינימלי. הסכום הוא בדיוק מספר הביטים שנדרש לקידוד הטקסט כולו, שכן עבור כל אות, אנו מכפילים את מספר המופעים שלה בטקסט במספר הביטים שנדרשים כדי לייצג אותה.

תיאור האלגוריתם[עריכת קוד מקור | עריכה]

האלגוריתם של האפמן הוא דוגמה לאלגוריתם חמדן. הוא מבצע בכל שלב את הפעולה שנראית, נקודתית, כפעולה המשתלמת ביותר, ומתבסס על כך שפתרון אופטימלי עבור \ n-1 אותיות יכול להפוך לפתרון אופטימלי עבור \ n אותיות.

הרעיון באלגוריתם הוא כזה: נבנה את העץ הבינארי של הקוד "מלמטה למעלה". ראשית, נמצא את שתי האותיות שמספר המופעים שלהם מינימלי, וניצור צומת חדש, כך ששתי האותיות הללו יהיו בניו. כעת נתייחס לצומת החדש בתור אות חדשה, שמספר המופעים שלה הוא סכום מספרי המופעים של שתי האותיות שהן בניה. כך קיבלנו צמצום של הבעיה מבעיה ב-\ n אותיות לבעיה ב-\ n-1 אותיות. נחזור על התהליך עד שנישאר עם צומת אחד בלבד - ראש העץ.

לצורך פעולתו, האלגוריתם זקוק למנגנון שיאפשר לו למצוא במהירות את שתי האותיות בעלות המופעים המינימליים. לצורך כך ניתן להיעזר בתור עדיפויות (שניתן לממש על ידי ערימה).

מבחינה פורמלית:

  1. צור תור עדיפויות \ Q שיכיל צמתים המייצגים את כל אותיות, ממויינות על פי מספר המופעים, מהקטן לגדול.
  2. כל עוד בתור העדיפויות יש יותר מצומת אחד, בצע:
    1. צור צומת חדש, \ z.
    2. הוצא מתור העדיפויות את שני האיברים העליונים, \ x,y.
    3. הפוך את \ x,y לבנים הימני והשמאלי של \ z.
    4. קבע את מספר המופעים של \ z להיות סכום מספרי המופעים של \ x ו-\ y.
    5. הכנס את \ z לתור העדיפויות.
  3. הצומת הבודד שנותר בתור העדיפויות הוא שורש עץ הקוד המבוקש.

סיבוכיות האלגוריתם היא \ O(n \log n) פעולות. אם האותיות נתונות בצורה ממוינת, ידוע אלגוריתם עם זמן ריצה לינארי למציאת קוד האפמן.

אופטימליות הקוד[עריכת קוד מקור | עריכה]

ניתן להוכיח כי קוד האפמן מחזיר תמיד קידוד אופטימלי, מבין כל צפני התחיליות. באופן כללי יותר, קוד האפמן נותן אורך ממוצע מינימלי של צופן בהשוואה לכל צפני החד-פענח (Uniquely Decodable). יש להיזהר בשימוש במילה "אופטימלי", שכן קיימים קידודים יעילים יותר במצבים מסוימים. האופטימליות של קוד האפמן פירושה שהוא הקוד היעיל ביותר שבו מייצגים כל אות על ידי רצף קבוע של ביטים, כך שלכל אות בטקסט מותאם רצף שכזה.

הוכחת אופטימליות הקוד אינה מסובכת, ומתבססת על כך שבהינתן קוד אופטימלי כלשהו, ניתן לבנות ממנו קוד אופטימלי חדש על ידי כך שמעבירים את שתי האותיות בעלות מספר המופעים הנמוך ביותר למקום הנמוך ביותר בעץ הקוד. מכיוון שפעולה זו יכולה רק לשפר את עלות הקוד הכוללת, הקוד נותר אופטימלי. כך ניתן באינדוקציה לבנות קוד האפמן מתוך כל קוד אופטימלי ללא הגדלת העלות.

קישורים חיצוניים[עריכת קוד מקור | עריכה]