Автор оригинала: Robin Andrews.
Это статья о Сложность времени в Python Programming. В нем мы исследуем то, что подразумевается под сложность временной сложности и показать, как такая же программа может быть значительно более или менее эффективным с точки зрения времени выполнения в зависимости от используемого алгоритма.
Темы охватывали:
- Что такое сложность времени в программировании Python?
- Обозначение “Big O”
- Построение графов времени сложности с Pyplot.
Сложность времени – это тема, которую многие самостоятельные программисты, которые не изучали компьютерную науку, как правило, уклоняются от. Однако стоит увлечь усилия, чтобы узнать, по крайней мере, основы этой темы, поскольку она позволит вам писать гораздо более эффективный код.
Предметом временной сложности в программировании может сначала казаться немного пугающим с некоторой незнакомой математической обозначением и различными графиками, которые используются для выражения времени, используемого для алгоритма для завершения, растет в размере его ввода.
Тем не мение:
Базовая концепция сложности времени проста: смотрите график времени выполнения на оси Y, нанесенные на грану от входного размера на оси X, мы хотим сохранить высоту значений Y как можно ниже, насколько это возможно, когда мы движемся по х. -ось.
Вы можете получить хорошее интуитивное понимание времени сложности, изучая графики различных математических функций и как высота графа растет, когда мы двигаемся вдоль оси X. График ниже показывает, как ведут себя различные типы математической функции. Идея состоит в том, что время выполнения алгоритмов можно увидеть, чтобы расти в аналогичной форме одной из этих типов, в зависимости от его реализации. Наша цель – написать алгоритмы, которые ведут себя как медленные растущие функции и избегают реализации, которые ведут себя как быстро растущие.
Есть много деталей, что вы можете войти в о том, рассматриваем ли мы лучший, худший, средний корпус и т. Д., Но это часто более подробно, чем вам нужно. Чтобы сохранить это просто, давайте просто скажем:
- экспоненциальный : очень плохо
- кубический : плохо, избегать, если это возможно
- квадратичный : плохо, избегать, если это возможно
- линейный : хорошо
- Логарифмический : большой
- Постоянный : ты ударил джекпот
Большой o Обозначение – это способ обращения к этим видам роста.
- O (2ⁿ): экспоненциальный
- O (n³): кубический
- O (n²): квадратичный
- O (n): линейный
- O (log n): логарифмический
- O (1): постоянная
Для оставшейся части этой статьи, вместо того, чтобы сосредоточиться на общей теории сложности времени, мы будем смотреть на конкретный алгоритм, который подсчитывает общие элементы в списке.
Посмотрите на этот график:
Вы можете ясно видеть на графике, как время выполнения count_common_elements_nested_loops () Растет намного быстрее, чем для count_common_elements_sets ()
Это использует Pyplot от Матплотлиб , мощная построенная библиотека для Python. Детали того, как использовать Pyplot Для другой статьи, но, изучая код ниже, вы можете получить чувство того, как он работает. Код использует Perf_Counter от время Библиотека для расчета времени выполнения разных алгоритмов для выполнения задачи подсчета общих элементов является список. Из полученного графика вы можете видеть, что существует значительная разница между реализациями с точки зрения сложности времени, по мере роста размера ввода к каждой функции.
Сложность времени Пример Python Code Listing
import random
import time
import matplotlib.pyplot as plt
MAX_LEN = 200 # Maximum length of input list.
def count_common_elements_nested_loops(l1, l2):
common_elements = []
count = 0
for v in l1:
for w in l2:
if w == v:
common_elements.append(w)
count += 1
return count
def count_common_elements_list_comp(l1, l2):
common_elements = [x for x in l1 if x in l2]
return len(common_elements)
def count_common_elements_sets(l1, l2):
common_elements = set(l1).intersection(l2)
return len(common_elements)
def count_common_elements_hash_table(l1, l2):
table = {}
common_elements = []
for v in l1:
table[v] = True
count = 0
for w in l2:
if table.get(w): # Avoid KeyError that would arise with table[w]
common_elements.append(w)
count += 1
return count
if __name__ == "__main__":
# Initialise results containers
lengths_nested = []
times_nested = []
lengths_comp = []
times_comp = []
lengths_hash_table = []
times_hash_table = []
lengths_sets = []
times_sets = []
for length in range(0, MAX_LEN, 10):
# Generate random lists
l1 = [random.randint(0, 99) for _ in range(length)]
l2 = [random.randint(0, 99) for _ in range(length)]
# Time execution for nested lists version
start = time.perf_counter()
count_common_elements_nested_loops(l1, l2)
end = time.perf_counter()
# Store results
lengths_nested.append(length)
times_nested.append(end - start)
# Time execution for list comprehension version
start = time.perf_counter()
count_common_elements_list_comp(l1, l2)
end = time.perf_counter()
# Store results
lengths_comp.append(length)
times_comp.append(end - start)
# Time execution for hash table version
start = time.perf_counter()
count_common_elements_hash_table(l1, l2)
end = time.perf_counter()
# Store results
lengths_hash_table.append(length)
times_hash_table.append(end - start)
# Time execution for sets version
start = time.perf_counter()
count_common_elements_sets(l1, l2)
end = time.perf_counter()
# Store results
lengths_sets.append(length)
times_sets.append(end - start)
# Plot results
plt.style.use("dark_background")
plt.figure().canvas.manager.set_window_title("Common List Elements Algorithm - Time Complexity")
plt.xlabel("List Length")
plt.ylabel("Execution Time (s)")
plt.plot(lengths_nested, times_nested, label="count_common_elements_nested_loops()")
plt.plot(lengths_comp, times_comp, label="count_common_elements_list_comp()")
plt.plot(lengths_hash_table, times_hash_table, label="count_common_elements_hash_table()")
plt.plot(lengths_sets, times_sets, label="count_common_elements_sets()")
plt.legend()
plt.tight_layout()
plt.show()
Некоторые наблюдения:
- Разница в производительности поражает, особенно со скоростью роста вложенного для версии петель …
- Возможно, вы ожидаете, что соприкосновения списка имеют одинаковую временную сложность для вложенных для циклов, поскольку сочетание списка могут быть созданы вложенные для циклов. Тем не менее, реализация понятных списков «под капотом» гораздо более эффективно.
- То же самое относится и к набору VS Hash_tables, поскольку наборы используют hash_tables. Однако метод Set. Internersection реализован в C. Стоит заменить то, что многие встроенные функции/методы почти всегда будут быстрее, чем эквивалентные алгоритмы, которые выполняются на уровне переводчика Python.
Эта статья предназначалась для того, чтобы дать вам практический опыт работы со сложностью времени в Python в качестве введения в тему. Сложность времени – это большой предмет, и есть много ресурсов, которые помогут вам узнать онлайн. Одно место, которое вы можете получить практику, на таких сайтах, как HackeRrank и Проект Эйлер , где оценка «грубой силы» может предоставить правильные ответы, но не в требуемом временном кадре.
Счастливые вычисления!