У меня есть три решения проблемы Leetcode, и я не совсем понимаю разницу во времени. Почему последняя функция в два раза быстрее первой?
68 мсdef numJewelsInStones(J, S):
count=0
for s in S:
if s in J:
count += 1
return count
40 мс def numJewelsInStones(J, S):
return sum(s in J for s in S)
32 мс def numJewelsInStones(J, S):
return len([x for x in S if x in J])
Почему последняя функция в два раза быстрее первой?
Аналитическая сложность времени в терминах больших обозначений О выглядит одинаково для всех, однако с учетом констант. То есть, например, O(n)
действительно означает O(c*n)
однако c
игнорируется соглашением при сравнении временных сложностей.
Каждая из ваших функций имеет разные c
. Особенно
sum
генератора, вероятно, выполняется в коде C (часть суммы, добавление чисел)len
- это простой атрибут "одиночная операция" для поиска в массиве, генерация которого занимает примерно столько же времени, а sum
n
операций добавления. Таким образом, c(for) > c(sum) > c(len)
где c(f)
- гипотетическое измерение фиксированных накладных расходов функции/оператора f
.
Вы можете проверить мои предположения, разобрав каждую функцию.
Кроме того, ваши измерения могут зависеть от изменений, вызванных другими процессами, работающими в вашей системе. Чтобы удалить эти влияния из вашего анализа, возьмите среднее время выполнения не менее 1000 вызовов для каждой функции (вы можете обнаружить, что, возможно, c
меньше, чем этот вариант, хотя я этого не ожидаю).
какова временная сложность этих функций?
Обратите внимание, что хотя все функции имеют одинаковую сложность O, последняя будет отличаться в зависимости от типа данных, который вы используете для J, S
Если J, S
имеют тип:
dict
, сложность ваших функций будет в O(n)
list
, сложность ваших функций будет в O(n*m)
, где n,m
- размеры переменных J, S
соответственно.set
, сложность ваших функций будет в O(n)
Зачем? Потому что Python in
operator на самом деле просто прокси для тестирования членства, реализованного для определенного типа. В частности. dict
и set
тестирование членства работает в O(1)
, который находится в постоянное время, в то время как один для list
работает в O(n)
время. Так как в случае list
есть пропуск на каждый член J
для каждого члена S
, или наоборот, общее время в O(n*m)
. Смотрите Python TimeComplexity wiki для подробностей.
С усложнением времени большие обозначения O описывают, как растет решение с ростом входного набора. Другими словами, как они относительно связаны. Если вашим решением является O (n), то по мере роста входных данных время завершения линейно возрастает. Конкретнее, если решением является O (n), и для набора данных, равного 100, требуется 10 секунд, то для набора данных, равного 1000, должно пройти примерно 100 секунд.
Ваше первое решение - O (n), мы знаем это из-за цикла for для s в S, который будет перебирать весь набор данных один раз. Если s в J, предполагая, что J является множеством или словарь, вероятно, будет иметь постоянное время, O (1), причина этого немного выходит за рамки вопроса. В результате первое общее решение - это O (n), линейное время.
Различия во времени между другими решениями, скорее всего, незначительны, если вы выполнили свои тесты для нескольких наборов данных и усреднили их по времени, учитывая время запуска и другие факторы, влияющие на результаты теста. Кроме того, обозначение Big O отбрасывает коэффициенты, например, O (3n) ~ = O (n).
Вы заметите, что во всех других решениях вы придерживаетесь той же концепции, перебираете всю коллекцию и проверяете существование в наборе или дикте. В результате все эти решения являются O (n). Различия во времени могут объясняться другими процессами, работающими в то же время, тем фактом, что некоторые из используемых встроенных компонентов являются чисто C, а также различиями в результате недостаточного тестирования.
Ну, вторая функция быстрее, чем первая из-за использования генератора вместо цикла. Третья функция быстрее второй, потому что генераторы второго суммирования выводят (который возвращает что-то вроде списка), а третьи - просто вычисляют его длину.
in
эксплуатации может иметь разные сложности в зависимости от структуры данных.