Какова временная сложность этих трех решений?

1

У меня есть три решения проблемы 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])
  • 0
    Обычно вы не измеряете временные характеристики только одним экспериментом, а вычисляете среднее значение за несколько прогонов, чтобы учесть любые случайные искажения. Кроме того, что такое J? in эксплуатации может иметь разные сложности в зависимости от структуры данных.
Теги:
time-complexity

3 ответа

2

Почему последняя функция в два раза быстрее первой?

Аналитическая сложность времени в терминах больших обозначений О выглядит одинаково для всех, однако с учетом констант. То есть, например, 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 для подробностей.

2

С усложнением времени большие обозначения 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, а также различиями в результате недостаточного тестирования.

0

Ну, вторая функция быстрее, чем первая из-за использования генератора вместо цикла. Третья функция быстрее второй, потому что генераторы второго суммирования выводят (который возвращает что-то вроде списка), а третьи - просто вычисляют его длину.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню