Я намагаюся навчити себе, як обчислити позначення BigO для довільної функції. Я знайшов цю функцію в підручнику. Книга стверджує, що функцією є O (n 2 ). Це дає пояснення, чому це відбувається, але я намагаюся слідувати. Цікаво, чи хтось міг би показати мені математику, чому це так. В принципі, я розумію, що це щось менше, ніж O (n 3 ), але я не міг самостійно приземлитися на O (n 2 )
Припустимо, нам дано три послідовності чисел, A, B і C. Будемо вважати, що жодна окрема послідовність не містить повторюваних значень, але можуть бути деякі числа, які знаходяться у двох-трьох послідовностях. Триполосна задача неперервності полягає в тому, щоб визначити, чи перетин трьох послідовностей порожній, а саме, що немає елемента x такий, що x ∈ A, x ∈ B і x ∈ C.
Між іншим, це не проблема домашнього завдання для мене - той корабель плив років тому:), просто я намагаюся бути розумнішим.
def disjoint(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
if a == b: # only check C if we found match from A and B
for c in C:
if a == c # (and thus a == b == c)
return False # we found a common value
return True # if we reach this, sets are disjoint
[Редагувати] Відповідно до підручника:
У вдосконаленій версії не просто ми економимо час, якщо нам пощастить. Ми стверджуємо, що найгірший час запуску для роз'єднання - O (n 2 ).
Пояснення книги, яке я намагаюся дотримуватися, таке:
Щоб врахувати загальний час роботи, ми вивчаємо час, витрачений на виконання кожного рядка коду. Управління циклом for над A вимагає часу O (n). Управління циклом for для B становить загальний час O (n 2 ), оскільки ця петля виконується n різних разів. Тест a == b оцінюється O (n 2 ) разів. Решта часу, що витрачається, залежить від кількості пар (a, b). Як ми зазначали, існує максимум n таких пар, і тому управління циклом над C, а команди всередині цього циклу використовують не більше O (n 2 ) часу. Загальний витрачений час - O (n 2 ).
(І щоб надати належний кредит ...) Книга така: Структури даних та алгоритми в Python Майкл Т. Гудріч та ін. всі, Wiley Publishing, стор. 135
[Редагувати] Виправдання; Нижче наведено код перед оптимізацією:
def disjoint1(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
for c in C:
if a == b == c:
return False # we found a common value
return True # if we reach this, sets are disjoint
У вищесказаному ви чітко бачите, що це O (n 3 ), оскільки кожна петля повинна виконуватись в повній мірі. Книга стверджує, що у спрощеному прикладі (подано перше) третя петля є лише складністю O (n 2 ), тому рівняння складності йде як k + O (n 2 ) + O (n 2 ), що в кінцевому підсумку дає O (n 2 ).
Хоча я не можу довести, що це так (таким чином, питання), читач може погодитися, що складність спрощеного алгоритму принаймні менша, ніж оригінал.
[Редагувати] І довести, що спрощена версія є квадратичною:
if __name__ == '__main__':
for c in [100, 200, 300, 400, 500]:
l1, l2, l3 = get_random(c), get_random(c), get_random(c)
start = time.time()
disjoint1(l1, l2, l3)
print(time.time() - start)
start = time.time()
disjoint2(l1, l2, l3)
print(time.time() - start)
Врожайність:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
Оскільки друга різниця дорівнює, спрощена функція дійсно є квадратичною:
[Редагувати] І ще більше підтвердження:
Якщо я припускаю найгірший випадок (A = B! = C),
if __name__ == '__main__':
for c in [10, 20, 30, 40, 50]:
l1, l2, l3 = range(0, c), range(0,c), range(5*c, 6*c)
its1 = disjoint1(l1, l2, l3)
its2 = disjoint2(l1, l2, l3)
print(f"iterations1 = {its1}")
print(f"iterations2 = {its2}")
disjoint2(l1, l2, l3)
врожайність:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
Використовуючи другий тест на різницю, найгірший результат - саме квадратичний.