Я пытаюсь научить себя, как рассчитать нотацию 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 over 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
При использовании второго разностного теста наихудший результат является точно квадратичным.