Вопрос производительности по нарезке строк в Python - PullRequest
2 голосов
/ 08 ноября 2019

Я изучаю некоторый Python, и в процессе этого я делаю несколько простых катов из кодовых войн. Я столкнулся с проблемой https://www.codewars.com/kata/scramblies.

Мое решение было следующим:

def scramble(s1,s2):
    result = True
    for character in s2:
        if character not in s1:
            return False
        i = s1.index(character)
        s1 = s1[0:i] + s1[i+1:]
    return result

Хотя это был правильный результат, он не был достаточно быстрым. Время моего решения истекло после 12000 мс. Я посмотрел на решения, представленные другими, и один из них включал создание набора.

  def scramble(s1,s2):
     for letter in set(s2):
        if s1.count(letter) < s2.count(letter):
          return False
     return True

Почему мое решение намного медленнее, чем другое? Похоже, что так и должно быть, если только я не неправильно понимаю эффективность среза строк. Мой подход к решению этой проблемы ошибочен или нет?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2019

Для такого рода задач онлайн-программирования с ограничением времени выполнения вашей программы входные данные теста будут включать в себя несколько довольно больших примеров, и обычно устанавливается ограничение по времени, чтобы вам не приходилось сжимать каждую последнюю миллисекунду производительностииз вашего кода, но вы должны написать алгоритм с достаточно низкой вычислительной сложностью . Чтобы ответить, почему ваш алгоритм истекает, мы можем проанализировать его, чтобы найти его вычислительную сложность, используя big O нотация .

Сначала мы можем пометить каждый отдельный оператор с его сложностью, где n этодлина s1 и m равна длине s2:

def scramble(s1,s2):
    result = True                  # O(1)
    for character in s2:           # loop runs O(m) times
        if character not in s1:        # O(n) to search characters in s1
            return False               # O(1)
        i = s1.index(character)        # O(n) to search characters in s1
        s1 = s1[0:i] + s1[i+1:]        # O(n) to build a new string
    return result                  # O(1)

Тогда общая сложность составляет O (1 + m * (n + 1 + n + n) + 1)или, проще говоря, O (m * n). Это не эффективно для этой проблемы.


Ключ к тому, почему альтернативный алгоритм работает быстрее, заключается в том, что set(s2) содержит только отличные символы из строки s2. Это важно, потому что алфавит, из которого образованы эти строки, имеет постоянный, ограниченный размер;предположительно 26 для строчных букв. Учитывая это, внешний цикл альтернативного алгоритма фактически выполняется не более 26 раз:

def scramble(s1,s2):
    for letter in set(s2):                      # O(m) to build a set 
                                                # loop runs O(1) times
        if s1.count(letter) < s2.count(letter):     # O(n) + O(m) to count
                                                    # chars from s1 and s2
            return False                            # O(1)
    return True                                 # O(1)

Это означает, что сложность альтернативного алгоритма составляет O (m + 1 * (n + m + 1) + 1) илиболее просто O (m + n), что означает, что он асимптотически более эффективен, чем ваш алгоритм.

0 голосов
/ 08 ноября 2019

Прежде всего, set быстр и очень хорош в своей работе. Для таких вещей, как in, set быстрее, чем list.

Во-вторых, ваше решение выполняет на больше работы, чем правильное решение. Обратите внимание, что второе решение никогда не изменяет s1 или s2, в то время как ваше решение принимает два среза из s1 и затем переназначает s1. Это вместе с звонком .index(). Нарезка не самая быстрая операция, в основном потому, что необходимо выделить память и скопировать данные. .remove(), вероятно, будет быстрее, чем комбинация .index() и нарезки, которые вы делаете.

Основное сообщение здесь состоит в том, что если задачу можно выполнить за меньшее количество операций, она, очевидно, будет выполняться быстрее. ,Нарезка также дороже, чем большинство других методов, потому что выделение пространства и копирование памяти является более дорогой операцией, чем вычислительные методы, такие как .count(), которые используются в правильном решении.

...