בעולם האמיתי

11/03/2022

נניח שיש לכם שתי רשימות ממוינות בפייתון ואתם צריכים לייצר מהן רשימה שלישית שתהיה ממוינת גם היא. כלומר בהינתן:

a = [10, 20, 33, 51, 94, 100]
b = [2, 3, 99, 102, 500]

צריך להדפיס את הרשימה:

[2, 3, 10, 20, 33, 51, 94, 99, 100, 102, 500]

נו, מה הבעיה אתם אומרים, אנחנו בפייתון, אז אפשר לכתוב פשוט:

def test1():
    return sorted([*a, *b])

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

def test2():
    i = 0
    j = 0
    output = []
    while True:
        if i >= len(a):
            output.extend(b[j:])
            break

        if j >= len(b):
            output.extend(a[i:])
            break

        next_a = a[i]
        next_b = b[j]

        if next_a < next_b:
            i += 1
            output.append(next_a)
        else:
            j += 1
            output.append(next_b)

    return output

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

print("Short solution took - ", timeit.timeit("test1()", globals=locals()))
print("Long smart solution tool - ", timeit.timeit("test2()", globals=locals()))

והתוצאה הלא ממש מפתיעה:

Short solution took -  0.26264833300956525
Long smart solution tool -  1.3138192089973018

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