Skip to content

מיטוב גנטי: מדריך תרמיל עם ישימות ופתרון ייחוס מדויק

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

מקור ההשראה הנקוב במקור הוא מסתו של Brian Potter ב־Construction Physics מ־11 ביוני 2026, Biological Evolution and Information Acquisition, ולא מאמר מחקר מזוהה. היא דנה בסימולציות מפושטות וברקומבינציה, עם הסתייגויות לגבי גנים שמשפיעים זה על זה. DNA כקוד וברירה כבדיקה הן כאן אנלוגיות לימודיות, לא זהויות מילוליות או סיפור מאומת של גילוי ב־Hacker News.

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

הבנת האנלוגיה: DNA לעומת קוד

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

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

NP-hard היא סיווג סיבוכיות למקרה הגרוע ביותר, ולא הוכחה שכל מופע בלתי פתיר בזמן חיי אדם. חיפוש גנטי אינו מבטיח איכות של 99.9% בתוך שלוש שניות. בדוגמה עם שמונה פריטים יש רק 256 תת־קבוצות, ולכן מנייה מלאה היא בסיס מדויק ופשוט להשוואה.

הארכיטקטורה של צינור אבולוציוני

חיפוש דורי טיפוסי מעריך מועמדים, בוחר הורים ויוצר צאצאים עד לעמידה בכלל עצירה. התרשים סכמטי: ברירה אינה פשוט משליכה כל פרט חלש, ושמירת הטוב ביותר שנמצא היא החלטת תכנון מפורשת.
מקרא התרשים: Initialize Population — אתחול אוכלוסייה; Generate random candidate solutions — יצירת מועמדים אקראיים; Evaluate Fitness Function — הערכת כשירות; Score each candidate’s performance — ניקוד כל מועמד; Selection Process — ברירה; Keep the best, discard the weak — ניסוח סכמטי של העדפת הטובים; Crossover & Mutation — הצלבה ומוטציה; Create next generation offspring — יצירת צאצאי הדור הבא; Loop until termination criteria met (e.g., 100 gens) — חזרה עד תנאי העצירה, למשל 100 דורות.

+------------------------------------------+
|          Initialize Population           |
|  (Generate random candidate solutions)   |
+--------------------+---------------------+
                     |
                     v
+--------------------+---------------------+
|        Evaluate Fitness Function        | <------+
|   (Score each candidate's performance)   |        |
+--------------------+---------------------+        |
                     |                              |
                     v                              |
+--------------------+---------------------+        |
|         Selection Process                |        |
| (Keep the best, discard the weak)        |        | Loop until
+--------------------+---------------------+        | termination
                     |                              | criteria met
                     v                              | (e.g., 100 gens)
+--------------------+---------------------+        |
|        Crossover & Mutation              |        |
|  (Create next generation offspring)      |        |
+--------------------+---------------------+        |
                     |                              |
                     +------------------------------+

בניית אלגוריתם גנטי ב־Python

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

זהו מודל לימודי של תרמיל 0/1. נחפש באופן היוריסטי ונשווה לפתרון ייחוס מדויק וקטן, בלי לטעון שההיוריסטיקה נחוצה או עדיפה.

שלב 1: הגדרת הבעיה והכרומוזום

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

import random
from itertools import product

# Synthetic independent optional items: not real services or measurements.
SERVICES = [
    {"name": "item-A", "memory": 2, "value": 10},
    {"name": "item-B", "memory": 5, "value": 30},
    {"name": "item-C", "memory": 10, "value": 50},
    {"name": "item-D", "memory": 7, "value": 35},
    {"name": "item-E", "memory": 3, "value": 15},
    {"name": "item-F", "memory": 1, "value": 5},
    {"name": "item-G", "memory": 1, "value": 2},
    {"name": "item-H", "memory": 12, "value": 60},
]
MAX_MEMORY = 20
POPULATION_SIZE = 50
GENERATIONS = 100
MUTATION_RATE = 0.05
rng = random.Random(42)

הכרומוזום מכיל שמונה סיביות. 1 כולל את הפריט הסינתטי המתאים ו־0 אינו כולל אותו. למשל, [1, 0, 1, 0, 0, 1, 1, 0] היא תת־קבוצה מועמדת, לא הצעת פריסה לייצור.

שלב 2: פונקציית הכשירות

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

def calculate_fitness(chromosome):
    if len(chromosome) != len(SERVICES) or any(g not in (0, 1) for g in chromosome):
        raise ValueError("invalid chromosome")
    memory = sum(g * s["memory"] for g, s in zip(chromosome, SERVICES))
    if memory > MAX_MEMORY:
        return -1
    return sum(g * s["value"] for g, s in zip(chromosome, SERVICES))

שלב 3: יצירת האוכלוסייה ההתחלתית וברירה

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

def generate_initial_population(size):
    if size < 3:
        raise ValueError("population must contain at least three candidates")
    population = [[0] * len(SERVICES)]  # Known feasible baseline.
    population += [[rng.randrange(2) for _ in SERVICES] for _ in range(size - 1)]
    return population

def select_parent(population, fitnesses):
    chosen = rng.sample(range(len(population)), 3)
    return population[max(chosen, key=lambda i: fitnesses[i])]

שלב 4: הצלבה ומוטציה

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

def crossover(parent1, parent2):
    point = rng.randrange(1, len(SERVICES))  # Fixed model has eight items.
    return parent1[:point] + parent2[point:]

def mutate(chromosome, mutation_rate):
    if not 0 <= mutation_rate <= 1:
        raise ValueError("mutation rate must be between zero and one")
    return [1 - g if rng.random() < mutation_rate else g for g in chromosome]

שלב 5: חיבור הכול ללולאה האבולוציונית

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

def run_evolution():
    population = generate_initial_population(POPULATION_SIZE)
    best = [0] * len(SERVICES)
    best_score = calculate_fitness(best)
    for _ in range(GENERATIONS):
        scores = [calculate_fitness(c) for c in population]
        index = max(range(len(population)), key=lambda i: scores[i])
        if scores[index] > best_score:
            best, best_score = population[index][:], scores[index]
        next_population = [best[:]]
        while len(next_population) < POPULATION_SIZE:
            child = crossover(select_parent(population, scores),
                              select_parent(population, scores))
            next_population.append(mutate(child, MUTATION_RATE))
        population = next_population
    # Evaluate offspring from the final generation as well.
    candidate = max(population, key=calculate_fitness)
    if calculate_fitness(candidate) > best_score:
        best, best_score = candidate[:], calculate_fitness(candidate)
    return best, best_score

if __name__ == "__main__":
    best, score = run_evolution()
    exact = max(product((0, 1), repeat=len(SERVICES)), key=calculate_fitness)
    optimum = calculate_fitness(exact)
    memory = sum(g * s["memory"] for g, s in zip(best, SERVICES))
    assert memory <= MAX_MEMORY
    assert score <= optimum
    print("Heuristic candidate:", best)
    print("Synthetic capacity used:", memory)
    print("Heuristic utility:", score)
    print("Exact small-instance utility:", optimum)
    print("Utility gap:", optimum - score)

למה זה חשוב למהנדסי תוכנה מודרניים

כיצד המודל הלימודי הזה מסייע לחשוב על עבודה הנדסית מציאותית יותר?

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

1. הרחבה אוטומטית ושיבוץ מכלים

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

2. אפיון ביצועים וכוונון מסדי נתונים

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

3. אבטחה והנדסת כאוס

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

סיכום: ההיגיון של הטבע במערכת שלכם

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

בחרו את שיטת החיפוש לאחר בדיקת גודל הבעיה ומבנהּ. במופעים קטנים, חיפוש מלא או שיטות מדויקות יכולים לספק ייחוס חשוב; במופעים גדולים, השוו היוריסטיקות לפי פערי איכות ועלויות הערכה שנמדדו.

עכשיו תורכם: מימשתם פעם אלגוריתמים גנטיים או מחשוב אבולוציוני בתהליכי צד השרת שלכם? באילו אסטרטגיות מיטוב משתמש הצוות לבעיות בעלות מרחב מצבים עצום? ספרו בתגובות למטה!

מקורות ראשוניים

Post a Comment

Previous Post Next Post